Fix #6165 (Error communicating with the device)

This commit is contained in:
Kovid Goyal 2010-07-13 12:12:03 -06:00
parent 60c86d4744
commit c56a4a5aaf
10 changed files with 169 additions and 195 deletions

View File

@ -1,24 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) --> <!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://web.resource.org/cc/" xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="128" width="128"
height="128" height="128"
id="svg1307" id="svg1307"
sodipodi:version="0.32" sodipodi:version="0.32"
inkscape:version="0.43" inkscape:version="0.47 r22583"
version="1.0" version="1.0"
sodipodi:docbase="/home/pinheiro/Documents/pics/new oxygen/svg" sodipodi:docname="donate.svg">
sodipodi:docname="love.svg">
<defs <defs
id="defs1309"> id="defs1309">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 64 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="128 : 64 : 1"
inkscape:persp3d-origin="64 : 42.666667 : 1"
id="perspective44" />
<linearGradient <linearGradient
inkscape:collect="always" inkscape:collect="always"
id="linearGradient2231"> id="linearGradient2231">
@ -180,8 +187,8 @@
inkscape:pageopacity="0.0" inkscape:pageopacity="0.0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="7.851329" inkscape:zoom="7.851329"
inkscape:cx="92.691163" inkscape:cx="60.937831"
inkscape:cy="92.473338" inkscape:cy="61.488995"
inkscape:current-layer="layer1" inkscape:current-layer="layer1"
showgrid="false" showgrid="false"
inkscape:document-units="px" inkscape:document-units="px"
@ -189,10 +196,11 @@
guidetolerance="0.1px" guidetolerance="0.1px"
showguides="true" showguides="true"
inkscape:guide-bbox="true" inkscape:guide-bbox="true"
inkscape:window-width="1106" inkscape:window-width="1680"
inkscape:window-height="958" inkscape:window-height="997"
inkscape:window-x="597" inkscape:window-x="-4"
inkscape:window-y="25"> inkscape:window-y="30"
inkscape:window-maximized="1">
<sodipodi:guide <sodipodi:guide
orientation="horizontal" orientation="horizontal"
position="32.487481" position="32.487481"
@ -245,26 +253,19 @@
id="path2276" id="path2276"
d="M 50.892799,3.2812959 L 50.892799,0.48658747 L 50.892799,3.2812959 z " d="M 50.892799,3.2812959 L 50.892799,0.48658747 L 50.892799,3.2812959 z "
style="fill:#ffffff;fill-opacity:0.75688076;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" /> style="fill:#ffffff;fill-opacity:0.75688076;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" />
<path
sodipodi:type="arc"
style="opacity:0.38139535;fill:url(#radialGradient3297);fill-opacity:1.0;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
id="path3289"
sodipodi:cx="63.912209"
sodipodi:cy="115.70919"
sodipodi:rx="63.912209"
sodipodi:ry="12.641975"
d="M 127.82442 115.70919 A 63.912209 12.641975 0 1 1 0,115.70919 A 63.912209 12.641975 0 1 1 127.82442 115.70919 z"
transform="matrix(1,0,0,0.416667,0,74.87151)" />
<path <path
style="fill:url(#radialGradient2335);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="fill:url(#radialGradient2335);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 35.325021,6.2016208 C 32.278871,6.2210338 29.045555,6.6687791 25.645673,7.6089386 C 5.9380713,13.058619 0.404709,29.342113 5.3805953,48.506873 C 12.126047,74.487157 36.855395,101.02725 64.150803,115.92895 L 64.150803,116.02417 C 64.162016,116.00826 64.173539,115.99248 64.184766,115.97656 C 64.195995,115.99248 64.207516,116.00826 64.218732,116.02417 L 64.218732,115.92895 C 90.473794,101.59521 116.24349,74.487157 122.98895,48.506873 C 127.96481,29.342113 122.43148,13.058619 102.72386,7.6089386 C 83.422254,2.2715258 69.549778,12.840101 64.184766,27.183808 C 59.764775,15.366673 49.572303,6.1108179 35.325021,6.2016208 z " d="M 35.325021,6.2016208 C 32.278871,6.2210338 29.045555,6.6687791 25.645673,7.6089386 C 5.9380713,13.058619 0.404709,29.342113 5.3805953,48.506873 C 12.126047,74.487157 36.855395,101.02725 64.150803,115.92895 L 64.150803,116.02417 C 64.162016,116.00826 64.173539,115.99248 64.184766,115.97656 C 64.195995,115.99248 64.207516,116.00826 64.218732,116.02417 L 64.218732,115.92895 C 90.473794,101.59521 116.24349,74.487157 122.98895,48.506873 C 127.96481,29.342113 122.43148,13.058619 102.72386,7.6089386 C 83.422254,2.2715258 69.549778,12.840101 64.184766,27.183808 C 59.764775,15.366673 49.572303,6.1108179 35.325021,6.2016208 z "
id="path2245" id="path2245"
sodipodi:nodetypes="cssccsccsscc" /> sodipodi:nodetypes="cssccsccsscc" />
<g
id="g2850">
<path <path
id="path2369" sodipodi:nodetypes="cssccsccsscc"
d="M 35.325021,6.2016208 C 32.278871,6.2210338 29.045555,6.6687791 25.645673,7.6089386 C 5.9380713,13.058619 0.404709,29.342113 5.3805953,48.506873 C 12.126047,74.487157 37.113186,101.16799 64.150803,115.92895 L 64.150803,116.02417 C 64.162016,116.00826 64.173539,115.99248 64.184766,115.97656 C 64.195995,115.99248 64.207516,116.00826 64.218732,116.02417 L 64.218732,115.92895 C 90.398445,101.63635 116.24349,74.487157 122.98895,48.506873 C 127.96481,29.342113 122.43148,13.058619 102.72386,7.6089386 C 83.422254,2.2715258 69.549778,12.840101 64.184766,27.183808 C 59.764775,15.366673 49.572303,6.1108179 35.325021,6.2016208 z "
style="opacity:0.4713115;fill:url(#linearGradient2379);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="opacity:0.4713115;fill:url(#linearGradient2379);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="cssccsccsscc" /> d="M 35.325021,6.2016208 C 32.278871,6.2210338 29.045555,6.6687791 25.645673,7.6089386 C 5.9380713,13.058619 0.404709,29.342113 5.3805953,48.506873 C 12.126047,74.487157 37.113186,101.16799 64.150803,115.92895 L 64.150803,116.02417 C 64.162016,116.00826 64.173539,115.99248 64.184766,115.97656 C 64.195995,115.99248 64.207516,116.00826 64.218732,116.02417 L 64.218732,115.92895 C 90.398445,101.63635 116.24349,74.487157 122.98895,48.506873 C 127.96481,29.342113 122.43148,13.058619 102.72386,7.6089386 C 83.422254,2.2715258 69.549778,12.840101 64.184766,27.183808 C 59.764775,15.366673 49.572303,6.1108179 35.325021,6.2016208 z "
id="path2369" />
</g>
<path <path
style="opacity:0.1762295;fill:url(#linearGradient2331);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.29999995;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" style="opacity:0.1762295;fill:url(#linearGradient2331);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.29999995;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 34.451605,6.2067207 C 31.659392,6.2976073 28.7301,6.7682297 25.648957,7.6202497 C 7.7889432,12.559022 1.5815371,26.389172 4.2759909,43.204304 C 27.13595,75.72273 65.297627,95.42612 91.41193,91.971053 C 105.43169,77.948778 119.04939,63.70497 122.99185,48.520401 C 127.96773,29.355639 122.42255,13.069929 102.71494,7.6202497 C 83.413331,2.2828362 69.546961,12.850845 64.181949,27.194552 C 59.761957,15.377418 49.555176,6.1159177 35.307894,6.2067207 C 35.022317,6.2085406 34.740456,6.1973187 34.451605,6.2067207 z " d="M 34.451605,6.2067207 C 31.659392,6.2976073 28.7301,6.7682297 25.648957,7.6202497 C 7.7889432,12.559022 1.5815371,26.389172 4.2759909,43.204304 C 27.13595,75.72273 65.297627,95.42612 91.41193,91.971053 C 105.43169,77.948778 119.04939,63.70497 122.99185,48.520401 C 127.96773,29.355639 122.42255,13.069929 102.71494,7.6202497 C 83.413331,2.2828362 69.546961,12.850845 64.181949,27.194552 C 59.761957,15.377418 49.555176,6.1159177 35.307894,6.2067207 C 35.022317,6.2085406 34.740456,6.1973187 34.451605,6.2067207 z "

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

BIN
resources/images/lt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -10,10 +10,10 @@ from base64 import b64decode
from uuid import uuid4 from uuid import uuid4
from lxml import etree from lxml import etree
from calibre import prints, guess_type from calibre import prints, guess_type, isbytestring
from calibre.devices.errors import DeviceError from calibre.devices.errors import DeviceError
from calibre.devices.usbms.driver import debug_print from calibre.devices.usbms.driver import debug_print
from calibre.constants import DEBUG from calibre.constants import DEBUG, preferred_encoding
from calibre.ebooks.chardet import xml_to_unicode from calibre.ebooks.chardet import xml_to_unicode
from calibre.ebooks.metadata import authors_to_string, title_sort from calibre.ebooks.metadata import authors_to_string, title_sort
@ -473,6 +473,13 @@ class XMLCache(object):
# if the case of a tie, and hope it is right. # if the case of a tie, and hope it is right.
timestamp = os.path.getmtime(path) timestamp = os.path.getmtime(path)
rec_date = record.get('date', None) rec_date = record.get('date', None)
def clean(x):
if isbytestring(x):
x = x.decode(preferred_encoding, 'replace')
x.replace(u'\0', '')
return x
if not getattr(book, '_new_book', False): # book is not new if not getattr(book, '_new_book', False): # book is not new
if strftime(timestamp, zone=time.gmtime) == rec_date: if strftime(timestamp, zone=time.gmtime) == rec_date:
gtz_count += 1 gtz_count += 1
@ -486,19 +493,19 @@ class XMLCache(object):
tz = time.gmtime tz = time.gmtime
debug_print("Using GMT TZ for new book", book.lpath) debug_print("Using GMT TZ for new book", book.lpath)
date = strftime(timestamp, zone=tz) date = strftime(timestamp, zone=tz)
record.set('date', date) record.set('date', clean(date))
record.set('size', str(os.stat(path).st_size)) record.set('size', clean(str(os.stat(path).st_size)))
title = book.title if book.title else _('Unknown') title = book.title if book.title else _('Unknown')
record.set('title', title) record.set('title', clean(title))
ts = book.title_sort ts = book.title_sort
if not ts: if not ts:
ts = title_sort(title) ts = title_sort(title)
record.set('titleSorter', ts) record.set('titleSorter', clean(ts))
if self.use_author_sort and book.author_sort is not None: if self.use_author_sort and book.author_sort is not None:
record.set('author', book.author_sort) record.set('author', clean(book.author_sort))
else: else:
record.set('author', authors_to_string(book.authors)) record.set('author', clean(authors_to_string(book.authors)))
ext = os.path.splitext(path)[1] ext = os.path.splitext(path)[1]
if ext: if ext:
ext = ext[1:].lower() ext = ext[1:].lower()
@ -506,7 +513,7 @@ class XMLCache(object):
if mime is None: if mime is None:
mime = guess_type('a.'+ext)[0] mime = guess_type('a.'+ext)[0]
if mime is not None: if mime is not None:
record.set('mime', mime) record.set('mime', clean(mime))
if 'sourceid' not in record.attrib: if 'sourceid' not in record.attrib:
record.set('sourceid', '1') record.set('sourceid', '1')
if 'id' not in record.attrib: if 'id' not in record.attrib:

View File

@ -765,6 +765,7 @@ class DeviceMixin(object): # {{{
self.book_details.reset_info() self.book_details.reset_info()
self.location_view.setCurrentIndex(self.location_view.model().index(0)) self.location_view.setCurrentIndex(self.location_view.model().index(0))
self.refresh_ondevice_info (device_connected = False) self.refresh_ondevice_info (device_connected = False)
self.tool_bar.device_status_changed(bool(connected))
def info_read(self, job): def info_read(self, job):
''' '''

View File

@ -334,7 +334,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
def __init__(self, parent, library_view, server=None): def __init__(self, parent, library_view, server=None):
ResizableDialog.__init__(self, parent) ResizableDialog.__init__(self, parent)
self.ICON_SIZES = {0:QSize(48, 48), 1:QSize(32,32), 2:QSize(24,24)}
self._category_model = CategoryModel() self._category_model = CategoryModel()
self.category_view.currentChanged = self.category_current_changed self.category_view.currentChanged = self.category_current_changed
@ -389,10 +388,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
self.add_custcol_button.clicked.connect(self.add_custcol) self.add_custcol_button.clicked.connect(self.add_custcol)
self.edit_custcol_button.clicked.connect(self.edit_custcol) self.edit_custcol_button.clicked.connect(self.edit_custcol)
icons = config['toolbar_icon_size']
self.toolbar_button_size.setCurrentIndex(0 if icons == self.ICON_SIZES[0] else 1 if icons == self.ICON_SIZES[1] else 2)
self.show_toolbar_text.setChecked(config['show_text_in_toolbar'])
output_formats = sorted(available_output_formats()) output_formats = sorted(available_output_formats())
output_formats.remove('oeb') output_formats.remove('oeb')
for f in output_formats: for f in output_formats:
@ -845,8 +840,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
must_restart = self.apply_custom_column_changes() must_restart = self.apply_custom_column_changes()
config['toolbar_icon_size'] = self.ICON_SIZES[self.toolbar_button_size.currentIndex()]
config['show_text_in_toolbar'] = bool(self.show_toolbar_text.isChecked())
config['separate_cover_flow'] = bool(self.separate_cover_flow.isChecked()) config['separate_cover_flow'] = bool(self.separate_cover_flow.isChecked())
config['disable_tray_notification'] = not self.systray_notifications.isChecked() config['disable_tray_notification'] = not self.systray_notifications.isChecked()
p = {0:'normal', 1:'high', 2:'low'}[self.priority.currentIndex()] p = {0:'normal', 1:'high', 2:'low'}[self.priority.currentIndex()]

View File

@ -422,54 +422,6 @@
</layout> </layout>
</item> </item>
<item row="10" column="0" colspan="2"> <item row="10" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Toolbar</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QComboBox" name="toolbar_button_size">
<item>
<property name="text">
<string>Large</string>
</property>
</item>
<item>
<property name="text">
<string>Medium</string>
</property>
</item>
<item>
<property name="text">
<string>Small</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>&amp;Button size in toolbar</string>
</property>
<property name="buddy">
<cstring>toolbar_button_size</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="show_toolbar_text">
<property name="text">
<string>Show &amp;text in toolbar buttons</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="11" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_7"> <layout class="QHBoxLayout" name="horizontalLayout_7">
<item> <item>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">

View File

@ -176,12 +176,6 @@ class ToolbarMixin(object): # {{{
def show_help(self, *args): def show_help(self, *args):
open_url(QUrl('http://calibre-ebook.com/user_manual')) open_url(QUrl('http://calibre-ebook.com/user_manual'))
def read_toolbar_settings(self):
self.tool_bar.setIconSize(config['toolbar_icon_size'])
self.tool_bar.setToolButtonStyle(
Qt.ToolButtonTextUnderIcon if \
config['show_text_in_toolbar'] else \
Qt.ToolButtonIconOnly)
# }}} # }}}

View File

@ -5,6 +5,8 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from operator import attrgetter
from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, QVariant, \ from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, QVariant, \
QAbstractListModel, QFont, QApplication, QPalette, pyqtSignal, QToolButton, \ QAbstractListModel, QFont, QApplication, QPalette, pyqtSignal, QToolButton, \
QModelIndex, QListView, QAbstractButton, QPainter, QPixmap, QColor, \ QModelIndex, QListView, QAbstractButton, QPainter, QPixmap, QColor, \
@ -13,41 +15,11 @@ from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, QVariant, \
from calibre.constants import __appname__, filesystem_encoding from calibre.constants import __appname__, filesystem_encoding
from calibre.gui2.search_box import SearchBox2, SavedSearchBox from calibre.gui2.search_box import SearchBox2, SavedSearchBox
from calibre.gui2.throbber import ThrobbingButton from calibre.gui2.throbber import ThrobbingButton
from calibre.gui2 import NONE from calibre.gui2 import NONE, config
from calibre.gui2.widgets import ComboBoxWithHelp from calibre.gui2.widgets import ComboBoxWithHelp
from calibre import human_readable from calibre import human_readable
class ToolBar(QToolBar): # {{{ ICON_SIZE = 48
def __init__(self, parent=None):
QToolBar.__init__(self, parent)
self.setContextMenuPolicy(Qt.PreventContextMenu)
self.setMovable(False)
self.setFloatable(False)
self.setOrientation(Qt.Horizontal)
self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea)
self.setIconSize(QSize(48, 48))
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
def add_actions(self, *args):
self.left_space = QWidget(self)
self.left_space.setSizePolicy(QSizePolicy.Expanding,
QSizePolicy.Minimum)
self.addWidget(self.left_space)
for action in args:
if action is None:
self.addSeparator()
else:
self.addAction(action)
self.right_space = QWidget(self)
self.right_space.setSizePolicy(QSizePolicy.Expanding,
QSizePolicy.Minimum)
self.addWidget(self.right_space)
def contextMenuEvent(self, *args):
pass
# }}}
# Location View {{{ # Location View {{{
@ -191,14 +163,15 @@ class LocationView(QListView):
self.setTabKeyNavigation(True) self.setTabKeyNavigation(True)
self.setProperty("showDropIndicator", True) self.setProperty("showDropIndicator", True)
self.setSelectionMode(self.SingleSelection) self.setSelectionMode(self.SingleSelection)
self.setIconSize(QSize(40, 40)) self.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
self.setMovement(self.Static) self.setMovement(self.Static)
self.setFlow(self.LeftToRight) self.setFlow(self.LeftToRight)
self.setGridSize(QSize(175, 90)) self.setGridSize(QSize(175, ICON_SIZE))
self.setViewMode(self.ListMode) self.setViewMode(self.ListMode)
self.setWordWrap(True) self.setWordWrap(True)
self.setObjectName("location_view") self.setObjectName("location_view")
self.setMaximumHeight(74) self.setMaximumSize(QSize(600, ICON_SIZE+16))
self.setMinimumWidth(400)
def eject_clicked(self, *args): def eject_clicked(self, *args):
self.unmount_device.emit() self.unmount_device.emit()
@ -207,6 +180,10 @@ class LocationView(QListView):
self.model().count = new_count self.model().count = new_count
self.model().reset() self.model().reset()
@property
def book_count(self):
return self.model().count
def current_changed(self, current, previous): def current_changed(self, current, previous):
if current.isValid(): if current.isValid():
i = current.row() i = current.row()
@ -248,12 +225,15 @@ class EjectButton(QAbstractButton):
def __init__(self, parent): def __init__(self, parent):
QAbstractButton.__init__(self, parent) QAbstractButton.__init__(self, parent)
self.mouse_over = False self.mouse_over = False
self.setMouseTracking(True)
def enterEvent(self, event): def enterEvent(self, event):
self.mouse_over = True self.mouse_over = True
QAbstractButton.enterEvent(self, event)
def leaveEvent(self, event): def leaveEvent(self, event):
self.mouse_over = False self.mouse_over = False
QAbstractButton.leaveEvent(self, event)
def paintEvent(self, event): def paintEvent(self, event):
painter = QPainter(self) painter = QPainter(self)
@ -344,33 +324,84 @@ class SearchBar(QWidget): # {{{
# }}} # }}}
class LocationBar(ToolBar): # {{{ class ToolBar(QToolBar): # {{{
def __init__(self, actions, donate, location_view, parent=None): def __init__(self, actions, donate, location_view, parent=None):
ToolBar.__init__(self, parent) QToolBar.__init__(self, parent)
self.setContextMenuPolicy(Qt.PreventContextMenu)
for ac in actions: self.setMovable(False)
self.addAction(ac) self.setFloatable(False)
self.setOrientation(Qt.Horizontal)
self.addWidget(location_view) self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea)
self.w = QWidget() self.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
self.w.setLayout(QVBoxLayout())
self.w.layout().addWidget(donate)
donate.setAutoRaise(True)
donate.setCursor(Qt.PointingHandCursor)
self.addWidget(self.w)
self.setIconSize(QSize(50, 50))
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
def button_for_action(self, ac): self.showing_device = False
b = QToolButton(self) self.all_actions = actions
b.setDefaultAction(ac) self.donate = donate
for x in ('ToolTip', 'StatusTip', 'WhatsThis'): self.location_view = location_view
getattr(b, 'set'+x)(b.text()) self.d_widget = QWidget()
self.d_widget.setLayout(QVBoxLayout())
self.d_widget.layout().addWidget(donate)
donate.setAutoRaise(True)
donate.setCursor(Qt.PointingHandCursor)
self.build_bar()
def contextMenuEvent(self, *args):
pass
def device_status_changed(self, connected):
self.showing_device = connected
self.build_bar()
def build_bar(self):
order_field = 'device' if self.showing_device else 'normal'
o = attrgetter(order_field+'_order')
sepvals = [2] if self.showing_device else [1]
sepvals += [3]
actions = [x for x in self.all_actions if o(x) > -1]
actions.sort(cmp=lambda x,y : cmp(o(x), o(y)))
self.clear()
for x in actions:
self.addAction(x)
ch = self.widgetForAction(x)
ch.setCursor(Qt.PointingHandCursor)
ch.setAutoRaise(True)
if x.action_name == 'choose_library':
self.location_action = self.addWidget(self.location_view)
self.choose_action = x
if config['show_donate_button']:
self.addWidget(self.d_widget)
if x.action_name not in ('choose_library', 'help'):
ch.setPopupMode(ch.MenuButtonPopup)
for x in actions:
if x.separator_before in sepvals:
self.insertSeparator(x)
self.location_action.setVisible(self.showing_device)
self.choose_action.setVisible(not self.showing_device)
def count_changed(self, new_count):
text = _('%d books')%new_count
a = self.choose_action
a.setText(text)
def resizeEvent(self, ev):
style = Qt.ToolButtonTextUnderIcon
if self.size().width() < 1260:
style = Qt.ToolButtonIconOnly
self.setToolButtonStyle(style)
QToolBar.resizeEvent(self, ev)
return b
# }}} # }}}
class Action(QAction):
pass
class MainWindowMixin(object): class MainWindowMixin(object):
def __init__(self): def __init__(self):
@ -385,12 +416,19 @@ class MainWindowMixin(object):
self.centralwidget.setLayout(self._central_widget_layout) self.centralwidget.setLayout(self._central_widget_layout)
self.resize(1012, 740) self.resize(1012, 740)
self.donate_button = ThrobbingButton(self.centralwidget) self.donate_button = ThrobbingButton(self.centralwidget)
self.donate_button.set_normal_icon_size(64, 64) self.donate_button.set_normal_icon_size(ICON_SIZE, ICON_SIZE)
# Actions {{{ # Actions {{{
def ac(name, text, icon, shortcut=None, tooltip=None): all_actions = []
action = QAction(QIcon(I(icon)), text, self)
def ac(normal_order, device_order, separator_before,
name, text, icon, shortcut=None, tooltip=None):
action = Action(QIcon(I(icon)), text, self)
action.normal_order = normal_order
action.device_order = device_order
action.separator_before = separator_before
action.action_name = name
text = tooltip if tooltip else text text = tooltip if tooltip else text
action.setToolTip(text) action.setToolTip(text)
action.setStatusTip(text) action.setStatusTip(text)
@ -400,56 +438,46 @@ class MainWindowMixin(object):
if shortcut: if shortcut:
action.setShortcut(shortcut) action.setShortcut(shortcut)
setattr(self, 'action_'+name, action) setattr(self, 'action_'+name, action)
all_actions.append(action)
ac('add', _('Add books'), 'add_book.svg', _('A')) ac(0, 7, 0, 'add', _('Add books'), 'add_book.svg', _('A'))
ac('del', _('Remove books'), 'trash.svg', _('Del')) ac(1, 1, 0, 'edit', _('Edit metadata'), 'edit_input.svg', _('E'))
ac('edit', _('Edit metadata'), 'edit_input.svg', _('E')) ac(2, 2, 3, 'convert', _('Convert books'), 'convert.svg', _('C'))
ac('merge', _('Merge book records'), 'merge_books.svg', _('M')) ac(3, 3, 0, 'view', _('View'), 'view.svg', _('V'))
ac('sync', _('Send to device'), 'sync.svg') ac(4, 4, 3, 'choose_library', _('%d books')%0, 'lt.png',
ac('save', _('Save to disk'), 'save.svg', _('S')) tooltip=_('Choose calibre library to work with'))
ac('news', _('Fetch news'), 'news.svg', _('F')) ac(5, 5, 3, 'news', _('Fetch news'), 'news.svg', _('F'))
ac('convert', _('Convert books'), 'convert.svg', _('C')) ac(6, 6, 0, 'save', _('Save to disk'), 'save.svg', _('S'))
ac('view', _('View'), 'view.svg', _('V')) ac(7, 0, 0, 'sync', _('Send to device'), 'sync.svg')
ac('open_containing_folder', _('Open containing folder'), ac(8, 8, 3, 'del', _('Remove books'), 'trash.svg', _('Del'))
ac(9, 9, 3, 'help', _('Help'), 'help.svg', _('F1'), _("Browse the calibre User Manual"))
ac(10, 10, 0, 'preferences', _('Preferences'), 'config.svg', _('Ctrl+P'))
ac(-1, -1, 0, 'merge', _('Merge book records'), 'merge_books.svg', _('M'))
ac(-1, -1, 0, 'open_containing_folder', _('Open containing folder'),
'document_open.svg') 'document_open.svg')
ac('show_book_details', _('Show book details'), ac(-1, -1, 0, 'show_book_details', _('Show book details'),
'dialog_information.svg') 'dialog_information.svg')
ac('books_by_same_author', _('Books by same author'), ac(-1, -1, 0, 'books_by_same_author', _('Books by same author'),
'user_profile.svg') 'user_profile.svg')
ac('books_in_this_series', _('Books in this series'), ac(-1, -1, 0, 'books_in_this_series', _('Books in this series'),
'books_in_series.svg') 'books_in_series.svg')
ac('books_by_this_publisher', _('Books by this publisher'), ac(-1, -1, 0, 'books_by_this_publisher', _('Books by this publisher'),
'publisher.png') 'publisher.png')
ac('books_with_the_same_tags', _('Books with the same tags'), ac(-1, -1, 0, 'books_with_the_same_tags', _('Books with the same tags'),
'tags.svg') 'tags.svg')
ac('preferences', _('Preferences'), 'config.svg', _('Ctrl+P'))
ac('help', _('Help'), 'help.svg', _('F1'), _("Browse the calibre User Manual"))
# }}} # }}}
self.tool_bar = ToolBar(self)
self.addToolBar(Qt.BottomToolBarArea, self.tool_bar)
self.tool_bar.add_actions(self.action_convert, self.action_view,
None, self.action_edit, None,
self.action_save, self.action_del,
None,
self.action_help, None, self.action_preferences)
self.location_view = LocationView(self.centralwidget) self.location_view = LocationView(self.centralwidget)
self.search_bar = SearchBar(self) self.search_bar = SearchBar(self)
self.location_bar = LocationBar([self.action_add, self.action_sync, self.tool_bar = ToolBar(all_actions, self.donate_button, self.location_view, self)
self.action_news], self.donate_button, self.location_view, self) self.addToolBar(Qt.TopToolBarArea, self.tool_bar)
self.addToolBar(Qt.TopToolBarArea, self.location_bar)
l = self.centralwidget.layout() l = self.centralwidget.layout()
l.addWidget(self.search_bar) l.addWidget(self.search_bar)
for ch in list(self.tool_bar.children()) + list(self.location_bar.children()):
if isinstance(ch, QToolButton):
ch.setCursor(Qt.PointingHandCursor)
ch.setAutoRaise(True)
if ch is not self.donate_button:
ch.setPopupMode(ch.MenuButtonPopup)
def read_toolbar_settings(self):
pass

View File

@ -13,6 +13,7 @@ class SearchRestrictionMixin(object):
self.search_restriction.setSizeAdjustPolicy(self.search_restriction.AdjustToMinimumContentsLengthWithIcon) self.search_restriction.setSizeAdjustPolicy(self.search_restriction.AdjustToMinimumContentsLengthWithIcon)
self.search_restriction.setMinimumContentsLength(10) self.search_restriction.setMinimumContentsLength(10)
self.search_restriction.setStatusTip(self.search_restriction.toolTip()) self.search_restriction.setStatusTip(self.search_restriction.toolTip())
self.search_count.setText(_("(all books)"))
''' '''
Adding and deleting books while restricted creates a complexity. When added, Adding and deleting books while restricted creates a complexity. When added,

View File

@ -167,8 +167,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{
self.eject_action = self.system_tray_menu.addAction( self.eject_action = self.system_tray_menu.addAction(
QIcon(I('eject.svg')), _('&Eject connected device')) QIcon(I('eject.svg')), _('&Eject connected device'))
self.eject_action.setEnabled(False) self.eject_action.setEnabled(False)
if not config['show_donate_button']:
self.donate_button.setVisible(False)
self.addAction(self.quit_action) self.addAction(self.quit_action)
self.action_restart = QAction(_('&Restart'), self) self.action_restart = QAction(_('&Restart'), self)
self.addAction(self.action_restart) self.addAction(self.action_restart)
@ -220,8 +218,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{
if self.system_tray_icon.isVisible() and opts.start_in_tray: if self.system_tray_icon.isVisible() and opts.start_in_tray:
self.hide_windows() self.hide_windows()
for t in (self.location_view, self.tool_bar):
self.library_view.model().count_changed_signal.connect \ self.library_view.model().count_changed_signal.connect \
(self.location_view.count_changed) (t.count_changed)
if not gprefs.get('quick_start_guide_added', False): if not gprefs.get('quick_start_guide_added', False):
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
mi = MetaInformation(_('Calibre Quick Start Guide'), ['John Schember']) mi = MetaInformation(_('Calibre Quick Start Guide'), ['John Schember'])
@ -274,8 +273,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{
SIGNAL('start_recipe_fetch(PyQt_PyObject)'), SIGNAL('start_recipe_fetch(PyQt_PyObject)'),
self.download_scheduled_recipe, Qt.QueuedConnection) self.download_scheduled_recipe, Qt.QueuedConnection)
self.location_view.setCurrentIndex(self.location_view.model().index(0))
self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection) self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection)
AddAction.__init__(self) AddAction.__init__(self)