diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index 88c9324239..2b7c476579 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -8,7 +8,7 @@ from calibre.constants import numeric_version from calibre.ptempfile import PersistentTemporaryFile -class Plugin(object): +class Plugin(object): # {{{ ''' A calibre plugin. Useful members include: @@ -147,9 +147,9 @@ class Plugin(object): if hasattr(it, '__exit__'): it.__exit__(*args) +# }}} - -class FileTypePlugin(Plugin): +class FileTypePlugin(Plugin): # {{{ ''' A plugin that is associated with a particular set of file types. ''' @@ -191,7 +191,9 @@ class FileTypePlugin(Plugin): # Default implementation does nothing return path_to_ebook -class MetadataReaderPlugin(Plugin): +# }}} + +class MetadataReaderPlugin(Plugin): # {{{ ''' A plugin that implements reading metadata from a set of file types. ''' @@ -219,8 +221,9 @@ class MetadataReaderPlugin(Plugin): :return: A :class:`calibre.ebooks.metadata.MetaInformation` object ''' return None +# }}} -class MetadataWriterPlugin(Plugin): +class MetadataWriterPlugin(Plugin): # {{{ ''' A plugin that implements reading metadata from a set of file types. ''' @@ -249,7 +252,9 @@ class MetadataWriterPlugin(Plugin): ''' pass -class CatalogPlugin(Plugin): +# }}} + +class CatalogPlugin(Plugin): # {{{ ''' A plugin that implements a catalog generator. ''' @@ -352,7 +357,9 @@ class CatalogPlugin(Plugin): raise NotImplementedError('CatalogPlugin.generate_catalog() default ' 'method, should be overridden in subclass') -class InterfaceActionBase(Plugin): +# }}} + +class InterfaceActionBase(Plugin): # {{{ supported_platforms = ['windows', 'osx', 'linux'] author = 'Kovid Goyal' @@ -360,3 +367,44 @@ class InterfaceActionBase(Plugin): can_be_disabled = False actual_plugin = None +# }}} + +class PreferencesPlugin(Plugin): # {{{ + + supported_platforms = ['windows', 'osx', 'linux'] + author = 'Kovid Goyal' + type = _('Preferences') + can_be_disabled = False + + #: Import path to module that contains a class named ConfigWidget + #: which implements the ConfigWidgetInterface. Used by + #: :meth:`create_widget`. + config_widget = None + + #: Where in the list of categories the :attr:`category` of this plugin should be. + category_order = 100 + + #: Where in the list of names in a category, the :attr:`gui_name` of this + #: plugin should be + name_order = 100 + + #: The category this plugin should be in + category = None + + #: The name displayed to the user for this plugin + gui_name = None + + def create_widget(self, parent=None): + ''' + Create and return the actual Qt widget used for setting this group of + preferences. The widget must implement the ConfigWidgetInterface. + + The default implementation uses :attr:`config_widget` to instantiate + the widget. + ''' + base = __import__(self.config_widget, fromlist=[1]) + widget = base.ConfigWidget(parent) + return widget + +# }}} + diff --git a/src/calibre/customize/ui.py b/src/calibre/customize/ui.py index b720964c92..265b42bad2 100644 --- a/src/calibre/customize/ui.py +++ b/src/calibre/customize/ui.py @@ -7,7 +7,8 @@ from contextlib import closing from calibre.customize import Plugin, CatalogPlugin, FileTypePlugin, \ MetadataReaderPlugin, MetadataWriterPlugin, \ - InterfaceActionBase as InterfaceAction + InterfaceActionBase as InterfaceAction, \ + PreferencesPlugin from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin from calibre.customize.profiles import InputProfile, OutputProfile from calibre.customize.builtins import plugins as builtin_plugins @@ -257,6 +258,17 @@ def interface_actions(): yield plugin # }}} +# Preferences Plugins # {{{ + +def preferences_plugins(): + customization = config['plugin_customization'] + for plugin in _initialized_plugins: + if isinstance(plugin, PreferencesPlugin): + if not is_disabled(plugin): + plugin.site_customization = customization.get(plugin.name, '') + yield plugin +# }}} + # Metadata read/write {{{ _metadata_readers = {} _metadata_writers = {} diff --git a/src/calibre/gui2/preferences/__init__.py b/src/calibre/gui2/preferences/__init__.py index 20ec932fb8..5f9535bb93 100644 --- a/src/calibre/gui2/preferences/__init__.py +++ b/src/calibre/gui2/preferences/__init__.py @@ -7,28 +7,67 @@ __docformat__ = 'restructuredtext en' from PyQt4.Qt import QWidget, pyqtSignal -class PreferenceWidget(QWidget): +from calibre.customize.ui import preferences_plugins - category = None - name = None +class ConfigWidgetInterface(object): - changed_signal = pyqtSignal() - - def __init__(self, parent=None): - QWidget.__init__(self, parent) - - self.has_changed = False - self.changed.connect(lambda : setattr(self, 'has_changed', True)) - self.setupUi(self) + changed_signal = None def genesis(self, gui): raise NotImplementedError() - def reset_to_defaults(self): + def restore_defaults(self): pass def commit(self): pass - def add_boolean(self, widget_name, preference_interface, pref_name): - pass + +class ConfigWidgetBase(QWidget, ConfigWidgetInterface): + + changed_signal = pyqtSignal() + + def __init__(self, parent=None): + QWidget.__init__(self, parent) + if hasattr(self, 'setupUi'): + self.setupUi(self) + +def get_plugin(category, name): + for plugin in preferences_plugins(): + if plugin.category == category and plugin.name == name: + return plugin + raise ValueError( + 'No Preferences PLugin with category: %s and name: %s found' % + (category, name)) + +def test_widget(category, name, gui=None): # {{{ + from PyQt4.Qt import QDialog, QVBoxLayout, QDialogButtonBox + pl = get_plugin(category, name) + d = QDialog() + d.resize(750, 550) + bb = QDialogButtonBox(d) + bb.setStandardButtons(bb.Apply|bb.Cancel|bb.RestoreDefaults) + bb.accepted.connect(d.accept) + bb.rejected.connect(d.reject) + w = pl.create_widget(d) + bb.button(bb.RestoreDefaults).clicked.connect(w.restore_defaults) + bb.button(bb.Apply).setEnabled(False) + w.changed_signal.connect(lambda : bb.button(bb.Apply).setEnable(True)) + l = QVBoxLayout() + pl.setLayout(l) + l.addWidget(w) + if gui is None: + from calibre.gui2.ui import Main + from calibre.gui2.main import option_parser + from calibre.library.db import db + parser = option_parser() + opts, args = parser.parse_args([]) + actions = tuple(Main.create_application_menubar()) + db = db() + gui = Main(opts) + gui.initialize(db.library_path, db, None, actions) + w.genesis(gui) + if d.exec_() == QDialog.Accepted: + w.commit() +# }}} + diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 41b166b13f..c2635e8b44 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -50,6 +50,8 @@ class Listener(Thread): # {{{ self.start() def run(self): + if self.listener is None: + return while self._run: try: conn = self.listener.accept()