mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 18:24:30 -04:00
New tabbed configuration for KoboTouch driver. With the new files.
This commit is contained in:
parent
5665f5e57e
commit
adc9535714
486
src/calibre/devices/kobo/kobotouch_config.py
Normal file
486
src/calibre/devices/kobo/kobotouch_config.py
Normal file
@ -0,0 +1,486 @@
|
||||
#!/usr/bin/env python2
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||
from __future__ import (unicode_literals, #division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import textwrap
|
||||
|
||||
from PyQt5.Qt import (QLabel, QGridLayout, QLineEdit, QVBoxLayout,
|
||||
QDialog, QDialogButtonBox, QCheckBox)
|
||||
|
||||
from calibre.gui2.device_drivers.tabbed_device_config import TabbedDeviceConfig, DeviceConfigTab, DeviceOptionsGroupBox
|
||||
from calibre.devices.usbms.driver import debug_print
|
||||
|
||||
def wrap_msg(msg):
|
||||
return textwrap.fill(msg.strip(), 100)
|
||||
|
||||
def setToolTipFor(widget, tt):
|
||||
widget.setToolTip(wrap_msg(tt))
|
||||
|
||||
def create_checkbox(title, tt, state):
|
||||
cb = QCheckBox(title)
|
||||
cb.setToolTip(wrap_msg(tt))
|
||||
cb.setChecked(bool(state))
|
||||
return cb
|
||||
|
||||
|
||||
class KOBOTOUCHConfig(TabbedDeviceConfig):
|
||||
|
||||
def __init__(self, device_settings, all_formats, supports_subdirs,
|
||||
must_read_metadata, supports_use_author_sort,
|
||||
extra_customization_message, device, extra_customization_choices=None, parent=None):
|
||||
|
||||
super(KOBOTOUCHConfig, self).__init__(device_settings, all_formats, supports_subdirs,
|
||||
must_read_metadata, supports_use_author_sort,
|
||||
extra_customization_message, device, extra_customization_choices, parent)
|
||||
|
||||
self.device_settings = device_settings
|
||||
self.all_formats = all_formats
|
||||
self.supports_subdirs = supports_subdirs
|
||||
self.must_read_metadata = must_read_metadata
|
||||
self.supports_use_author_sort = supports_use_author_sort
|
||||
self.extra_customization_message = extra_customization_message
|
||||
self.extra_customization_choices = extra_customization_choices
|
||||
|
||||
self.current_device_key = device.device_defaults_key
|
||||
|
||||
self.tab1 = Tab1Config(self, self.device)
|
||||
self.tab2 = Tab2Config(self, self.device)
|
||||
|
||||
extra_tab_pos = self.indexOf(self.extra_tab)
|
||||
last_tab_pos = self.insertTab(extra_tab_pos, self.tab1, _("Collections, Covers && Uploads"))
|
||||
last_tab_pos = self.insertTab(last_tab_pos + 1, self.tab2, _('Metadata && Advanced'))
|
||||
|
||||
|
||||
def get_pref(self, key):
|
||||
return self.device.get_pref(key)
|
||||
|
||||
@property
|
||||
def device(self):
|
||||
return self._device()
|
||||
|
||||
def validate(self):
|
||||
if hasattr(self, 'formats'):
|
||||
if not self.formats.validate():
|
||||
return False
|
||||
if not self.template.validate():
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def book_uploads_options(self):
|
||||
return self.tab1.book_uploads_options
|
||||
|
||||
@property
|
||||
def collections_options(self):
|
||||
return self.tab1.collections_options
|
||||
|
||||
@property
|
||||
def cover_options(self):
|
||||
return self.tab1.covers_options
|
||||
|
||||
@property
|
||||
def device_list_options(self):
|
||||
return self.tab2.device_list_options
|
||||
|
||||
@property
|
||||
def advanced_options(self):
|
||||
return self.tab2.advanced_options
|
||||
|
||||
@property
|
||||
def metadata_options(self):
|
||||
return self.tab2.metadata_options
|
||||
|
||||
def commit(self):
|
||||
debug_print("KOBOTOUCHConfig::commit: start")
|
||||
p = super(KOBOTOUCHConfig, self).commit()
|
||||
|
||||
p['manage_collections'] = self.manage_collections
|
||||
p['create_collections'] = self.create_collections
|
||||
p['collections_columns'] = self.collections_columns
|
||||
p['delete_empty_collections'] = self.delete_empty_collections
|
||||
|
||||
p['upload_covers'] = self.upload_covers
|
||||
p['keep_cover_aspect'] = self.keep_cover_aspect
|
||||
p['upload_grayscale'] = self.upload_grayscale
|
||||
|
||||
p['show_recommendations'] = self.show_recommendations
|
||||
p['show_previews'] = self.show_previews
|
||||
p['show_archived_books'] = self.show_archived_books
|
||||
|
||||
p['update_series'] = self.update_series
|
||||
p['modify_css'] = self.modify_css
|
||||
|
||||
p['support_newer_firmware'] = self.support_newer_firmware
|
||||
p['debugging_title'] = self.debugging_title
|
||||
|
||||
p['extra_customization'] = self.extra_tab.extra_customization()
|
||||
|
||||
return p
|
||||
|
||||
|
||||
class Tab1Config(DeviceConfigTab): # {{{
|
||||
|
||||
def __init__(self, parent, device):
|
||||
super(Tab1Config, self).__init__(parent)
|
||||
|
||||
self.l = QVBoxLayout(self)
|
||||
self.setLayout(self.l)
|
||||
|
||||
self.collections_options = CollectionsGroupBox(self, device)
|
||||
self.l.addWidget(self.collections_options)
|
||||
self.add_widget(self.collections_options)
|
||||
|
||||
self.covers_options = CoversGroupBox(self, device)
|
||||
self.l.addWidget(self.covers_options)
|
||||
self.add_widget(self.covers_options)
|
||||
|
||||
self.book_uploads_options = BookUploadsGroupBox(self, device)
|
||||
self.l.addWidget(self.book_uploads_options)
|
||||
self.add_widget(self.book_uploads_options)
|
||||
# }}}
|
||||
|
||||
class Tab2Config(DeviceConfigTab): # {{{
|
||||
|
||||
def __init__(self, parent, device):
|
||||
super(Tab2Config, self).__init__(parent)
|
||||
|
||||
self.l = QVBoxLayout(self)
|
||||
self.setLayout(self.l)
|
||||
|
||||
self.metadata_options = MetadataGroupBox(self, device)
|
||||
self.l.addWidget(self.metadata_options)
|
||||
self.add_widget(self.metadata_options)
|
||||
|
||||
self.device_list_options = DeviceListGroupBox(self, device)
|
||||
self.l.addWidget(self.device_list_options)
|
||||
self.add_widget(self.device_list_options)
|
||||
|
||||
self.advanced_options = AdvancedGroupBox(self, device)
|
||||
self.l.addWidget(self.advanced_options)
|
||||
self.add_widget(self.advanced_options)
|
||||
# }}}
|
||||
|
||||
|
||||
class BookUploadsGroupBox(DeviceOptionsGroupBox):
|
||||
|
||||
def __init__(self, parent, device):
|
||||
super(BookUploadsGroupBox, self).__init__(parent, device)
|
||||
self.setTitle(_("Book Uploading"))
|
||||
|
||||
self.options_layout = QGridLayout()
|
||||
self.options_layout.setObjectName("options_layout")
|
||||
self.setLayout(self.options_layout)
|
||||
|
||||
self.modify_css_checkbox = create_checkbox(
|
||||
_("Modify CSS"),
|
||||
_('This allows addition of user CSS rules and removal of some CSS. '
|
||||
'When sending a book, the driver adds the contents of {0} to all stylesheets in the ePub. '
|
||||
'This file is searched for in the root directory of the main memory of the device. '
|
||||
'As well as this, if the file contains settings for the "orphans" or "widows", '
|
||||
'these are removed for all styles in the original stylesheet.').format(device.KOBO_EXTRA_CSSFILE),
|
||||
device.get_pref('modify_css')
|
||||
)
|
||||
|
||||
self.options_layout.addWidget(self.modify_css_checkbox, 0, 0, 1, 2)
|
||||
self.options_layout.setRowStretch(1, 1)
|
||||
|
||||
@property
|
||||
def modify_css(self):
|
||||
return self.modify_css_checkbox.isChecked()
|
||||
|
||||
|
||||
class CollectionsGroupBox(DeviceOptionsGroupBox):
|
||||
|
||||
def __init__(self, parent, device):
|
||||
super(CollectionsGroupBox, self).__init__(parent, device)
|
||||
self.setTitle(_("Collections"))
|
||||
|
||||
self.options_layout = QGridLayout()
|
||||
self.options_layout.setObjectName("options_layout")
|
||||
self.setLayout(self.options_layout)
|
||||
|
||||
self.manage_collections_checkbox = create_checkbox(
|
||||
_("Manage Collections"),
|
||||
_('Create new bookshelves on the Kobo if they do not exist. This is only for firmware V2.0.0 or later.'),
|
||||
device.get_pref('manage_collections')
|
||||
)
|
||||
self.manage_collections_checkbox.clicked.connect(self.manage_collections_checkbox_clicked)
|
||||
|
||||
self.collections_columns_label = QLabel(_('Collections Columns'))
|
||||
self.collections_columns_edit = QLineEdit(self)
|
||||
self.collections_columns_edit.setToolTip(_('The Kobo from firmware V2.0.0 supports bookshelves.'
|
||||
' These are created on the Kobo. ' +
|
||||
'Specify a tags type column for automatic management.'))
|
||||
debug_print("CollectionsGroupBox::__init__ - device.settings()=", device.settings())
|
||||
self.collections_columns_edit.setText(device.get_pref('collections_columns'))
|
||||
|
||||
self.create_collections_checkbox = create_checkbox(
|
||||
_("Create Collections"),
|
||||
_('Create new bookshelves on the Kobo if they do not exist. This is only for firmware V2.0.0 or later.'),
|
||||
device.get_pref('create_collections')
|
||||
)
|
||||
self.delete_empty_collections_checkbox = create_checkbox(
|
||||
_('Delete Empty Bookshelves'),
|
||||
_('Delete any empty bookshelves from the Kobo when syncing is finished. This is only for firmware V2.0.0 or later.'),
|
||||
device.get_pref('delete_empty_collections')
|
||||
)
|
||||
|
||||
self.options_layout.addWidget(self.manage_collections_checkbox, 0, 0, 1, 1)
|
||||
self.options_layout.addWidget(self.collections_columns_label, 1, 0, 1, 1)
|
||||
self.options_layout.addWidget(self.collections_columns_edit, 1, 1, 1, 1)
|
||||
self.options_layout.addWidget(self.create_collections_checkbox, 2, 0, 1, 1)
|
||||
self.options_layout.addWidget(self.delete_empty_collections_checkbox, 2, 1, 1, 1)
|
||||
self.options_layout.setRowStretch(5, 1)
|
||||
|
||||
self.manage_collections_checkbox_clicked(self.manage_collections)
|
||||
|
||||
@property
|
||||
def manage_collections(self):
|
||||
return self.manage_collections_checkbox.isChecked()
|
||||
|
||||
@property
|
||||
def collections_columns(self):
|
||||
return self.collections_columns_edit.text().strip()
|
||||
|
||||
@property
|
||||
def create_collections(self):
|
||||
return self.create_collections_checkbox.isChecked()
|
||||
|
||||
@property
|
||||
def delete_empty_collections(self):
|
||||
return self.delete_empty_collections_checkbox.isChecked()
|
||||
|
||||
def manage_collections_checkbox_clicked(self, checked):
|
||||
self.collections_columns_label.setEnabled(checked)
|
||||
self.collections_columns_edit.setEnabled(checked)
|
||||
self.create_collections_checkbox.setEnabled(checked)
|
||||
self.delete_empty_collections_checkbox.setEnabled(checked)
|
||||
|
||||
|
||||
class CoversGroupBox(DeviceOptionsGroupBox):
|
||||
|
||||
def __init__(self, parent, device):
|
||||
super(CoversGroupBox, self).__init__(parent, device)
|
||||
self.setTitle(_("Covers"))
|
||||
|
||||
self.options_layout = QGridLayout()
|
||||
self.options_layout.setObjectName("options_layout")
|
||||
self.setLayout(self.options_layout)
|
||||
|
||||
self.upload_covers_checkbox = create_checkbox(
|
||||
_("Upload covers for books"),
|
||||
_('Upload cover images from the calibre library when sending books to the device.'),
|
||||
device.get_pref('upload_covers')
|
||||
)
|
||||
self.upload_covers_checkbox.clicked.connect(self.upload_covers_checkbox_clicked)
|
||||
|
||||
self.upload_grayscale_checkbox = create_checkbox(
|
||||
_('Upload Black and White Covers'),
|
||||
_('Convert covers to Black and White when uploading'),
|
||||
device.get_pref('upload_grayscale')
|
||||
)
|
||||
|
||||
self.keep_cover_aspect_checkbox = create_checkbox(
|
||||
_('Keep cover aspect ratio'),
|
||||
_('When uploading covers, do not change the aspect ratio when resizing for the device.'
|
||||
' This is for firmware versions 2.3.1 and later.'),
|
||||
device.get_pref('keep_cover_aspect'))
|
||||
|
||||
self.options_layout.addWidget(self.upload_covers_checkbox, 0, 0, 1, 2)
|
||||
self.options_layout.addWidget(self.keep_cover_aspect_checkbox, 1, 0, 1, 1)
|
||||
self.options_layout.addWidget(self.upload_grayscale_checkbox, 1, 1, 1, 1)
|
||||
self.options_layout.setRowStretch(2, 1)
|
||||
|
||||
self.upload_covers_checkbox_clicked(self.upload_covers)
|
||||
|
||||
@property
|
||||
def upload_covers(self):
|
||||
return self.upload_covers_checkbox.isChecked()
|
||||
|
||||
@property
|
||||
def upload_grayscale(self):
|
||||
return self.upload_grayscale_checkbox.isChecked()
|
||||
|
||||
@property
|
||||
def keep_cover_aspect(self):
|
||||
return self.keep_cover_aspect_checkbox.isChecked()
|
||||
|
||||
def upload_covers_checkbox_clicked(self, checked):
|
||||
self.upload_grayscale_checkbox.setEnabled(checked)
|
||||
self.keep_cover_aspect_checkbox.setEnabled(checked)
|
||||
|
||||
|
||||
class DeviceListGroupBox(DeviceOptionsGroupBox):
|
||||
|
||||
def __init__(self, parent, device):
|
||||
super(DeviceListGroupBox, self).__init__(parent, device)
|
||||
self.setTitle(_("Show as on device"))
|
||||
|
||||
self.options_layout = QGridLayout()
|
||||
self.options_layout.setObjectName("options_layout")
|
||||
self.setLayout(self.options_layout)
|
||||
|
||||
self.show_recommendations_checkbox = create_checkbox(
|
||||
_("Show Recommendations"),
|
||||
_('Kobo shows recommendations on the device. In some cases these have '
|
||||
'files but in other cases they are just pointers to the web site to buy. '
|
||||
'Enable if you wish to see/delete them.'),
|
||||
device.get_pref('show_recommendations')
|
||||
)
|
||||
|
||||
self.show_archived_books_checkbox = create_checkbox(
|
||||
_("Show archived books"),
|
||||
_('Archived books are listed on the device but need to be downloaded to read.'
|
||||
' Use this option to show these books and match them with books in the calibre library.'),
|
||||
device.get_pref('show_archived_books')
|
||||
)
|
||||
|
||||
self.show_previews_checkbox = create_checkbox(
|
||||
_('Show Previews'),
|
||||
_('Kobo previews are included on the Touch and some other versions'
|
||||
' by default they are no longer displayed as there is no good reason to '
|
||||
'see them. Enable if you wish to see/delete them.'),
|
||||
device.get_pref('show_previews')
|
||||
)
|
||||
|
||||
self.options_layout.addWidget(self.show_recommendations_checkbox, 0, 0, 1, 1)
|
||||
self.options_layout.addWidget(self.show_archived_books_checkbox, 0, 1, 1, 1)
|
||||
self.options_layout.addWidget(self.show_previews_checkbox, 1, 0, 1, 1)
|
||||
self.options_layout.setRowStretch(1, 1)
|
||||
|
||||
@property
|
||||
def show_recommendations(self):
|
||||
return self.show_recommendations_checkbox.isChecked()
|
||||
|
||||
@property
|
||||
def show_archived_books(self):
|
||||
return self.show_archived_books_checkbox.isChecked()
|
||||
|
||||
@property
|
||||
def show_previews(self):
|
||||
return self.show_previews_checkbox.isChecked()
|
||||
|
||||
|
||||
class AdvancedGroupBox(DeviceOptionsGroupBox):
|
||||
|
||||
def __init__(self, parent, device):
|
||||
super(AdvancedGroupBox, self).__init__(parent, device, _("Advanced Options"))
|
||||
# self.setTitle(_("Advanced Options"))
|
||||
|
||||
self.options_layout = QGridLayout()
|
||||
self.options_layout.setObjectName("options_layout")
|
||||
self.setLayout(self.options_layout)
|
||||
|
||||
self.support_newer_firmware_checkbox = create_checkbox(
|
||||
_("Attempt to support newer firmware"),
|
||||
_('Kobo routinely updates the firmware and the '
|
||||
'database version. With this option Calibre will attempt '
|
||||
'to perform full read-write functionality - Here be Dragons!! '
|
||||
'Enable only if you are comfortable with restoring your kobo '
|
||||
'to factory defaults and testing software. '
|
||||
'This driver supports firmware V2.x.x and DBVersion up to ') + unicode(device.supported_dbversion),
|
||||
device.get_pref('support_newer_firmware')
|
||||
)
|
||||
|
||||
self.debugging_title_checkbox = create_checkbox(
|
||||
_("Title to test when debugging"),
|
||||
_('Part of title of a book that can be used when doing some tests for debugging. '
|
||||
'The test is to see if the string is contained in the title of a book. '
|
||||
'The better the match, the less extraneous output.'),
|
||||
device.get_pref('debugging_title')
|
||||
)
|
||||
self.debugging_title_label = QLabel(_('Title to test when debugging'))
|
||||
self.debugging_title_edit = QLineEdit(self)
|
||||
self.debugging_title_edit.setToolTip(_('Part of title of a book that can be used when doing some tests for debugging. '
|
||||
'The test is to see if the string is contained in the title of a book. '
|
||||
'The better the match, the less extraneous output.'))
|
||||
self.debugging_title_edit.setText(device.get_pref('debugging_title'))
|
||||
self.debugging_title_label.setBuddy(self.debugging_title_edit)
|
||||
|
||||
self.options_layout.addWidget(self.support_newer_firmware_checkbox, 0, 0, 1, 2)
|
||||
self.options_layout.addWidget(self.debugging_title_label, 1, 0, 1, 1)
|
||||
self.options_layout.addWidget(self.debugging_title_edit, 1, 1, 1, 1)
|
||||
self.options_layout.setRowStretch(1, 2)
|
||||
|
||||
@property
|
||||
def support_newer_firmware(self):
|
||||
return self.support_newer_firmware_checkbox.isChecked()
|
||||
|
||||
@property
|
||||
def debugging_title(self):
|
||||
return self.debugging_title_edit.text().strip()
|
||||
|
||||
|
||||
class MetadataGroupBox(DeviceOptionsGroupBox):
|
||||
|
||||
def __init__(self, parent, device):
|
||||
super(MetadataGroupBox, self).__init__(parent, device)
|
||||
self.setTitle(_("Metadata Options"))
|
||||
|
||||
self.options_layout = QGridLayout()
|
||||
self.options_layout.setObjectName("options_layout")
|
||||
self.setLayout(self.options_layout)
|
||||
|
||||
self.update_device_metadata_checkbox = create_checkbox(
|
||||
_("Update metadata on the device"),
|
||||
_('Update the metadata on the device when it is connected. '
|
||||
'Be careful when doing this as it will take time and could make the initial connection take a long time.'),
|
||||
device.get_pref('update_device_metadata')
|
||||
)
|
||||
self.options_layout.addWidget(self.update_device_metadata_checkbox, 0, 0, 1, 2)
|
||||
self.update_device_metadata_checkbox.clicked.connect(self.update_device_metadata_checkbox_clicked)
|
||||
|
||||
self.update_series_checkbox = create_checkbox(
|
||||
_("Set Series information"),
|
||||
_('The book lists on the Kobo devices can display series information. '
|
||||
'This is not read by the device from the sideloaded books. '
|
||||
'Series information can only be added to the device after the book has been processed by the device. '
|
||||
'Enable if you wish to set series information.'),
|
||||
device.get_pref('update_series')
|
||||
)
|
||||
self.options_layout.addWidget(self.update_series_checkbox, 1, 0, 1, 2)
|
||||
self.options_layout.setRowStretch(1, 1)
|
||||
|
||||
@property
|
||||
def update_series(self):
|
||||
return self.update_series_checkbox.isChecked()
|
||||
|
||||
@property
|
||||
def update_device_metadata(self):
|
||||
return self.update_series_checkbox.isChecked()
|
||||
|
||||
def update_device_metadata_checkbox_clicked(self, checked):
|
||||
self.update_series_checkbox.setEnabled(checked)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from calibre.gui2 import Application
|
||||
from calibre.devices.kobo.driver import KOBOTOUCH
|
||||
from calibre.devices.scanner import DeviceScanner
|
||||
s = DeviceScanner()
|
||||
s.scan()
|
||||
app = Application([])
|
||||
debug_print("KOBOTOUCH:", KOBOTOUCH)
|
||||
dev = KOBOTOUCH(None)
|
||||
# dev.startup()
|
||||
# cd = dev.detect_managed_devices(s.devices)
|
||||
# dev.open(cd, 'test')
|
||||
cw = dev.config_widget()
|
||||
d = QDialog()
|
||||
d.l = QVBoxLayout()
|
||||
d.setLayout(d.l)
|
||||
d.l.addWidget(cw)
|
||||
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
|
||||
d.l.addWidget(bb)
|
||||
bb.accepted.connect(d.accept)
|
||||
bb.rejected.connect(d.reject)
|
||||
if d.exec_() == d.Accepted:
|
||||
cw.commit()
|
||||
dev.shutdown()
|
||||
|
||||
|
408
src/calibre/gui2/device_drivers/tabbed_device_config.py
Normal file
408
src/calibre/gui2/device_drivers/tabbed_device_config.py
Normal file
@ -0,0 +1,408 @@
|
||||
#!/usr/bin/env python2
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||
from __future__ import (unicode_literals, #division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import weakref, textwrap
|
||||
|
||||
from PyQt5.Qt import (QWidget, QListWidgetItem, Qt, QToolButton, QLabel,
|
||||
QTabWidget, QGridLayout, QListWidget, QIcon, QLineEdit, QVBoxLayout,
|
||||
QPushButton, QGroupBox, QScrollArea, QHBoxLayout, QComboBox,
|
||||
pyqtSignal, QSizePolicy, QDialog, QDialogButtonBox, QPlainTextEdit,
|
||||
QApplication, QSize, QCheckBox, QSpacerItem)
|
||||
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
from calibre.gui2.device_drivers.mtp_config import (FormatsConfig, TemplateConfig)
|
||||
from calibre.devices.usbms.driver import debug_print
|
||||
|
||||
def wrap_msg(msg):
|
||||
return textwrap.fill(msg.strip(), 100)
|
||||
|
||||
def setToolTipFor(widget, tt):
|
||||
widget.setToolTip(wrap_msg(tt))
|
||||
|
||||
def create_checkbox(title, tt, state):
|
||||
cb = QCheckBox(title)
|
||||
cb.setToolTip(wrap_msg(tt))
|
||||
cb.setChecked(bool(state))
|
||||
return cb
|
||||
|
||||
|
||||
class TabbedDeviceConfig(QTabWidget):
|
||||
"""
|
||||
This is a generic Tabbed Device config widget. It designed for devices with more
|
||||
complex configuration. But, it is backwards compatible to the standard device
|
||||
configuration widget.
|
||||
|
||||
The configuration made up of two default tabs plus extra tabs as needed for the
|
||||
device. The extra tabs are defined as part of the subclass of this widget for
|
||||
the device.
|
||||
|
||||
The two default tabs are the "File Formats" and "Extra Customization". These
|
||||
tabs are the same as the two sections of the standard device configuration
|
||||
widget. The second of these tabs will only be created if the device driver has
|
||||
extra configuration options. All options on these tabs work the same way as for
|
||||
the standard device configuration widget.
|
||||
|
||||
When implementing a subclass for a device driver, create tabs, subclassed from
|
||||
DeviceConfigTab, for each set of options. Within the tabs, group boxes, subclassed
|
||||
from DeviceOptionsGroupBox, are created to further group the options. The group
|
||||
boxes can be coded to support any control type and dependencies between them.
|
||||
"""
|
||||
def __init__(self, device_settings, all_formats, supports_subdirs,
|
||||
must_read_metadata, supports_use_author_sort,
|
||||
extra_customization_message, device,
|
||||
extra_customization_choices=None, parent=None):
|
||||
QTabWidget.__init__(self, parent)
|
||||
self._device = weakref.ref(device)
|
||||
|
||||
self.device_settings = device_settings
|
||||
self.all_formats = set(all_formats)
|
||||
self.supports_subdirs = supports_subdirs
|
||||
self.must_read_metadata = must_read_metadata
|
||||
self.supports_use_author_sort = supports_use_author_sort
|
||||
self.extra_customization_message = extra_customization_message
|
||||
self.extra_customization_choices = extra_customization_choices
|
||||
|
||||
try:
|
||||
self.device_name = device.get_gui_name()
|
||||
except TypeError:
|
||||
self.device_name = getattr(device, 'gui_name', None) or _('Device')
|
||||
|
||||
if device.USER_CAN_ADD_NEW_FORMATS:
|
||||
self.all_formats = set(self.all_formats) | set(BOOK_EXTENSIONS)
|
||||
|
||||
self.base = QWidget(self)
|
||||
# self.insertTab(0, self.base, _('Configure %s') % self.device.current_friendly_name)
|
||||
self.insertTab(0, self.base, _("File Formats"))
|
||||
l = self.base.l = QGridLayout(self.base)
|
||||
self.base.setLayout(l)
|
||||
|
||||
self.formats = FormatsConfig(self.all_formats, device_settings.format_map)
|
||||
if device.HIDE_FORMATS_CONFIG_BOX:
|
||||
self.formats.hide()
|
||||
|
||||
self.opt_use_subdirs = create_checkbox(
|
||||
_("Use sub-directories"),
|
||||
_('Place files in sub-directories if the device supports them'),
|
||||
device_settings.use_subdirs
|
||||
)
|
||||
self.opt_read_metadata = create_checkbox(
|
||||
_("Read metadata from files on device"),
|
||||
_('Read metadata from files on device'),
|
||||
device_settings.read_metadata
|
||||
)
|
||||
|
||||
self.template = TemplateConfig(device_settings.save_template)
|
||||
self.opt_use_author_sort = create_checkbox(
|
||||
_("Use author sort for author"),
|
||||
_("Use author sort for author"),
|
||||
device_settings.read_metadata
|
||||
)
|
||||
self.opt_use_author_sort.setObjectName("opt_use_author_sort")
|
||||
self.base.la = la = QLabel(_(
|
||||
'Choose the formats to send to the %s')%self.device_name)
|
||||
la.setWordWrap(True)
|
||||
|
||||
l.addWidget(la, 1, 0, 1, 1)
|
||||
l.addWidget(self.formats, 2, 0, 1, 1)
|
||||
l.addWidget(self.opt_read_metadata, 3, 0, 1, 1)
|
||||
l.addWidget(self.opt_use_subdirs, 4, 0, 1, 1)
|
||||
l.addWidget(self.opt_use_author_sort, 5, 0, 1, 1)
|
||||
l.addWidget(self.template, 6, 0, 1, 1)
|
||||
l.setRowStretch(2, 10)
|
||||
|
||||
if device.HIDE_FORMATS_CONFIG_BOX:
|
||||
self.formats.hide()
|
||||
|
||||
if supports_subdirs:
|
||||
self.opt_use_subdirs.setChecked(device_settings.use_subdirs)
|
||||
else:
|
||||
self.opt_use_subdirs.hide()
|
||||
if not must_read_metadata:
|
||||
self.opt_read_metadata.setChecked(device_settings.read_metadata)
|
||||
else:
|
||||
self.opt_read_metadata.hide()
|
||||
if supports_use_author_sort:
|
||||
self.opt_use_author_sort.setChecked(device_settings.use_author_sort)
|
||||
else:
|
||||
self.opt_use_author_sort.hide()
|
||||
|
||||
|
||||
self.extra_tab = ExtraCustomization(self.extra_customization_message,
|
||||
self.extra_customization_choices,
|
||||
self.device_settings)
|
||||
# Only display the extra customization tab if there are options on it.
|
||||
if self.extra_tab.has_extra_customizations:
|
||||
self.addTab(self.extra_tab, _('Extra Customization'))
|
||||
|
||||
self.setCurrentIndex(0)
|
||||
|
||||
def __getattr__(self, attr_name):
|
||||
"If the object doesn't have an attribute, then check each tab."
|
||||
try:
|
||||
return super(TabbedDeviceConfig, self).__getattr__(attr_name)
|
||||
except AttributeError as ae:
|
||||
for i in range(0, self.count()):
|
||||
atab = self.widget(i)
|
||||
try:
|
||||
return getattr(atab, attr_name)
|
||||
except AttributeError:
|
||||
pass
|
||||
raise ae
|
||||
|
||||
def get_pref(self, key):
|
||||
debug_print("get_pref - self.device.prefs", self.device.prefs)
|
||||
p = self.device.prefs.get(self.current_device_key, {})
|
||||
if not p:
|
||||
self.device.prefs[self.current_device_key] = p
|
||||
debug_print("get_pref - self.device.get_pref(key)", self.device.get_pref(key))
|
||||
return self.device.get_pref(key)
|
||||
|
||||
@property
|
||||
def device(self):
|
||||
return self._device()
|
||||
|
||||
def format_map(self):
|
||||
# formats = [unicode(self.columns.item(i).data(Qt.UserRole) or '') for i in range(self.columns.count()) if self.columns.item(i).checkState()==Qt.Checked]
|
||||
return self.formats.format_map
|
||||
|
||||
def use_subdirs(self):
|
||||
return self.opt_use_subdirs.isChecked()
|
||||
|
||||
def read_metadata(self):
|
||||
return self.opt_read_metadata.isChecked()
|
||||
|
||||
def use_author_sort(self):
|
||||
return self.opt_use_author_sort.isChecked()
|
||||
|
||||
@property
|
||||
def opt_save_template(self):
|
||||
# Really shouldn't be accessing the template this way
|
||||
return self.template.t
|
||||
|
||||
def text(self):
|
||||
# Really shouldn't be accessing the template this way
|
||||
return self.template.t.text()
|
||||
|
||||
@property
|
||||
def opt_extra_customization(self):
|
||||
return self.extra_tab.opt_extra_customization
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return self.opt_save_template
|
||||
|
||||
def validate(self):
|
||||
if hasattr(self, 'formats'):
|
||||
if not self.formats.validate():
|
||||
return False
|
||||
if not self.template.validate():
|
||||
return False
|
||||
return True
|
||||
|
||||
def commit(self):
|
||||
debug_print("TabbedDeviceConfig::commit: start")
|
||||
p = self.device._configProxy()
|
||||
debug_print("commit: starting setting=%s" % (p, ))
|
||||
|
||||
f = self.formats.format_map
|
||||
debug_print("commit: self.formats.format_map=", self.formats.format_map)
|
||||
debug_print("commit: self.device.prefs['format_map']=", self.device.prefs['format_map'])
|
||||
if f and f != self.device.prefs['format_map']:
|
||||
p['format_map'] = f
|
||||
|
||||
f = self.use_subdirs()
|
||||
if f != self.get_pref('use_subdirs'):
|
||||
p['use_subdirs'] = f
|
||||
|
||||
f = self.read_metadata()
|
||||
if f != self.get_pref('read_metadata'):
|
||||
p['read_metadata'] = f
|
||||
|
||||
t = self.template.template
|
||||
if t and t != self.device.prefs['save_template']:
|
||||
p['save_template'] = t
|
||||
|
||||
p['extra_customization'] = self.extra_tab.extra_customization()
|
||||
|
||||
return p
|
||||
|
||||
|
||||
class DeviceConfigTab(QWidget): # {{{
|
||||
'''
|
||||
This is an abstraction for a tab in the configuration. The main reason for it is to
|
||||
abstract the properties of the configuration tab. When a property is accessed, it
|
||||
will iterate over all known widgets looking for the property.
|
||||
'''
|
||||
def __init__(self, parent=None):
|
||||
QWidget.__init__(self)
|
||||
self.parent = parent
|
||||
|
||||
self.widgets = []
|
||||
|
||||
def add_widget(self, widget):
|
||||
self.widgets.append(widget)
|
||||
|
||||
def __getattr__(self, attr_name):
|
||||
try:
|
||||
return super(DeviceConfigTab, self).__getattr__(attr_name)
|
||||
except AttributeError as ae:
|
||||
for awidget in self.widgets:
|
||||
try:
|
||||
return getattr(awidget, attr_name)
|
||||
except AttributeError:
|
||||
pass
|
||||
raise ae
|
||||
|
||||
|
||||
|
||||
class ExtraCustomization(DeviceConfigTab): # {{{
|
||||
def __init__(self, extra_customization_message, extra_customization_choices, device_settings):
|
||||
super(ExtraCustomization, self).__init__()
|
||||
|
||||
debug_print("ExtraCustomization.__init__ - extra_customization_message=", extra_customization_message)
|
||||
debug_print("ExtraCustomization.__init__ - extra_customization_choices=", extra_customization_choices)
|
||||
debug_print("ExtraCustomization.__init__ - device_settings.extra_customization=", device_settings.extra_customization)
|
||||
debug_print("ExtraCustomization.__init__ - device_settings=", device_settings)
|
||||
self.extra_customization_message = extra_customization_message
|
||||
|
||||
self.l = QVBoxLayout(self)
|
||||
self.setLayout(self.l)
|
||||
|
||||
options_group = QGroupBox(_("Extra driver customization options"), self)
|
||||
self.l.addWidget(options_group)
|
||||
self.extra_layout = QGridLayout()
|
||||
self.extra_layout.setObjectName("extra_layout")
|
||||
options_group.setLayout(self.extra_layout)
|
||||
|
||||
if extra_customization_message:
|
||||
extra_customization_choices = extra_customization_choices or {}
|
||||
def parse_msg(m):
|
||||
msg, _, tt = m.partition(':::') if m else ('', '', '')
|
||||
return msg.strip(), textwrap.fill(tt.strip(), 100)
|
||||
|
||||
if isinstance(extra_customization_message, list):
|
||||
self.opt_extra_customization = []
|
||||
if len(extra_customization_message) > 6:
|
||||
row_func = lambda x, y: ((x/2) * 2) + y
|
||||
col_func = lambda x: x%2
|
||||
else:
|
||||
row_func = lambda x, y: x*2 + y
|
||||
col_func = lambda x: 0
|
||||
|
||||
for i, m in enumerate(extra_customization_message):
|
||||
label_text, tt = parse_msg(m)
|
||||
if not label_text:
|
||||
self.opt_extra_customization.append(None)
|
||||
continue
|
||||
if isinstance(device_settings.extra_customization[i], bool):
|
||||
self.opt_extra_customization.append(QCheckBox(label_text))
|
||||
self.opt_extra_customization[-1].setToolTip(tt)
|
||||
self.opt_extra_customization[i].setChecked(bool(device_settings.extra_customization[i]))
|
||||
elif i in extra_customization_choices:
|
||||
cb = QComboBox(self)
|
||||
self.opt_extra_customization.append(cb)
|
||||
l = QLabel(label_text)
|
||||
l.setToolTip(tt), cb.setToolTip(tt), l.setBuddy(cb), cb.setToolTip(tt)
|
||||
for li in sorted(extra_customization_choices[i]):
|
||||
self.opt_extra_customization[i].addItem(li)
|
||||
cb.setCurrentIndex(max(0, cb.findText(device_settings.extra_customization[i])))
|
||||
else:
|
||||
self.opt_extra_customization.append(QLineEdit(self))
|
||||
l = QLabel(label_text)
|
||||
l.setToolTip(tt)
|
||||
self.opt_extra_customization[i].setToolTip(tt)
|
||||
l.setBuddy(self.opt_extra_customization[i])
|
||||
l.setWordWrap(True)
|
||||
self.opt_extra_customization[i].setText(device_settings.extra_customization[i])
|
||||
self.opt_extra_customization[i].setCursorPosition(0)
|
||||
self.extra_layout.addWidget(l, row_func(i + 2, 0), col_func(i))
|
||||
self.extra_layout.addWidget(self.opt_extra_customization[i],
|
||||
row_func(i + 2, 1), col_func(i))
|
||||
spacerItem1 = QSpacerItem(10, 10, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
||||
self.extra_layout.addItem(spacerItem1, row_func(i + 2 + 2, 1), 0, 1, 2)
|
||||
self.extra_layout.setRowStretch(row_func(i + 2 + 2, 1), 2)
|
||||
else:
|
||||
self.opt_extra_customization = QLineEdit()
|
||||
label_text, tt = parse_msg(extra_customization_message)
|
||||
l = QLabel(label_text)
|
||||
l.setToolTip(tt)
|
||||
l.setBuddy(self.opt_extra_customization)
|
||||
l.setWordWrap(True)
|
||||
if device_settings.extra_customization:
|
||||
self.opt_extra_customization.setText(device_settings.extra_customization)
|
||||
self.opt_extra_customization.setCursorPosition(0)
|
||||
self.opt_extra_customization.setCursorPosition(0)
|
||||
self.extra_layout.addWidget(l, 0, 0)
|
||||
self.extra_layout.addWidget(self.opt_extra_customization, 1, 0)
|
||||
|
||||
def extra_customization(self):
|
||||
ec = []
|
||||
if self.extra_customization_message:
|
||||
if isinstance(self.extra_customization_message, list):
|
||||
for i in range(0, len(self.extra_customization_message)):
|
||||
if self.opt_extra_customization[i] is None:
|
||||
ec.append(None)
|
||||
continue
|
||||
if hasattr(self.opt_extra_customization[i], 'isChecked'):
|
||||
ec.append(self.opt_extra_customization[i].isChecked())
|
||||
elif hasattr(self.opt_extra_customization[i], 'currentText'):
|
||||
ec.append(unicode(self.opt_extra_customization[i].currentText()).strip())
|
||||
else:
|
||||
ec.append(unicode(self.opt_extra_customization[i].text()).strip())
|
||||
else:
|
||||
ec = unicode(self.opt_extra_customization.text()).strip()
|
||||
if not ec:
|
||||
ec = None
|
||||
|
||||
return ec
|
||||
|
||||
@property
|
||||
def has_extra_customizations(self):
|
||||
debug_print("ExtraCustomization::has_extra_customizations - self.extra_customization_message", self.extra_customization_message)
|
||||
return self.extra_customization_message and len(self.extra_customization_message) > 0
|
||||
|
||||
# }}}
|
||||
|
||||
class DeviceOptionsGroupBox(QGroupBox):
|
||||
"""
|
||||
This is a container for the individual options for a device driver.
|
||||
"""
|
||||
def __init__(self, parent, device=None, title=_("Unknown")):
|
||||
QGroupBox.__init__(self, parent)
|
||||
|
||||
self.device = device
|
||||
self.setTitle(title)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from calibre.gui2 import Application
|
||||
from calibre.devices.kobo.driver import KOBO
|
||||
from calibre.devices.scanner import DeviceScanner
|
||||
s = DeviceScanner()
|
||||
s.scan()
|
||||
app = Application([])
|
||||
dev = KOBO(None)
|
||||
debug_print("KOBO:", KOBO)
|
||||
# dev.startup()
|
||||
# cd = dev.detect_managed_devices(s.devices)
|
||||
# dev.open(cd, 'test')
|
||||
cw = dev.config_widget()
|
||||
d = QDialog()
|
||||
d.l = QVBoxLayout()
|
||||
d.setLayout(d.l)
|
||||
d.l.addWidget(cw)
|
||||
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
|
||||
d.l.addWidget(bb)
|
||||
bb.accepted.connect(d.accept)
|
||||
bb.rejected.connect(d.reject)
|
||||
if d.exec_() == d.Accepted:
|
||||
cw.commit()
|
||||
dev.shutdown()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user