Framework for plugin based preferences dialog

This commit is contained in:
Kovid Goyal 2010-08-24 18:13:13 -06:00
parent 81f081527d
commit 19bcbb713f
4 changed files with 123 additions and 22 deletions

View File

@ -8,7 +8,7 @@ from calibre.constants import numeric_version
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
class Plugin(object): class Plugin(object): # {{{
''' '''
A calibre plugin. Useful members include: A calibre plugin. Useful members include:
@ -147,9 +147,9 @@ class Plugin(object):
if hasattr(it, '__exit__'): if hasattr(it, '__exit__'):
it.__exit__(*args) it.__exit__(*args)
# }}}
class FileTypePlugin(Plugin): # {{{
class FileTypePlugin(Plugin):
''' '''
A plugin that is associated with a particular set of file types. A plugin that is associated with a particular set of file types.
''' '''
@ -191,7 +191,9 @@ class FileTypePlugin(Plugin):
# Default implementation does nothing # Default implementation does nothing
return path_to_ebook return path_to_ebook
class MetadataReaderPlugin(Plugin): # }}}
class MetadataReaderPlugin(Plugin): # {{{
''' '''
A plugin that implements reading metadata from a set of file types. 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: A :class:`calibre.ebooks.metadata.MetaInformation` object
''' '''
return None return None
# }}}
class MetadataWriterPlugin(Plugin): class MetadataWriterPlugin(Plugin): # {{{
''' '''
A plugin that implements reading metadata from a set of file types. A plugin that implements reading metadata from a set of file types.
''' '''
@ -249,7 +252,9 @@ class MetadataWriterPlugin(Plugin):
''' '''
pass pass
class CatalogPlugin(Plugin): # }}}
class CatalogPlugin(Plugin): # {{{
''' '''
A plugin that implements a catalog generator. A plugin that implements a catalog generator.
''' '''
@ -352,7 +357,9 @@ class CatalogPlugin(Plugin):
raise NotImplementedError('CatalogPlugin.generate_catalog() default ' raise NotImplementedError('CatalogPlugin.generate_catalog() default '
'method, should be overridden in subclass') 'method, should be overridden in subclass')
class InterfaceActionBase(Plugin): # }}}
class InterfaceActionBase(Plugin): # {{{
supported_platforms = ['windows', 'osx', 'linux'] supported_platforms = ['windows', 'osx', 'linux']
author = 'Kovid Goyal' author = 'Kovid Goyal'
@ -360,3 +367,44 @@ class InterfaceActionBase(Plugin):
can_be_disabled = False can_be_disabled = False
actual_plugin = None 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
# }}}

View File

@ -7,7 +7,8 @@ from contextlib import closing
from calibre.customize import Plugin, CatalogPlugin, FileTypePlugin, \ from calibre.customize import Plugin, CatalogPlugin, FileTypePlugin, \
MetadataReaderPlugin, MetadataWriterPlugin, \ MetadataReaderPlugin, MetadataWriterPlugin, \
InterfaceActionBase as InterfaceAction InterfaceActionBase as InterfaceAction, \
PreferencesPlugin
from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin
from calibre.customize.profiles import InputProfile, OutputProfile from calibre.customize.profiles import InputProfile, OutputProfile
from calibre.customize.builtins import plugins as builtin_plugins from calibre.customize.builtins import plugins as builtin_plugins
@ -257,6 +258,17 @@ def interface_actions():
yield plugin 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 read/write {{{
_metadata_readers = {} _metadata_readers = {}
_metadata_writers = {} _metadata_writers = {}

View File

@ -7,28 +7,67 @@ __docformat__ = 'restructuredtext en'
from PyQt4.Qt import QWidget, pyqtSignal from PyQt4.Qt import QWidget, pyqtSignal
class PreferenceWidget(QWidget): from calibre.customize.ui import preferences_plugins
category = None class ConfigWidgetInterface(object):
name = None
changed_signal = pyqtSignal() changed_signal = None
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)
def genesis(self, gui): def genesis(self, gui):
raise NotImplementedError() raise NotImplementedError()
def reset_to_defaults(self): def restore_defaults(self):
pass pass
def commit(self): def commit(self):
pass 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()
# }}}

View File

@ -50,6 +50,8 @@ class Listener(Thread): # {{{
self.start() self.start()
def run(self): def run(self):
if self.listener is None:
return
while self._run: while self._run:
try: try:
conn = self.listener.accept() conn = self.listener.accept()