diff --git a/src/calibre/ebooks/metadata/sources/base.py b/src/calibre/ebooks/metadata/sources/base.py
index 67a80d5785..5089d8951b 100644
--- a/src/calibre/ebooks/metadata/sources/base.py
+++ b/src/calibre/ebooks/metadata/sources/base.py
@@ -131,7 +131,22 @@ def fixcase(x):
x = titlecase(x)
return x
+class Option(object):
+ __slots__ = ['type', 'default', 'label', 'desc', 'name', 'choices']
+ def __init__(self, name, type_, default, label, desc, choices=None):
+ '''
+ :param name: The name of this option. Must be a valid python identifier
+ :param type_: The type of this option, one of ('number', 'string',
+ 'bool', 'choices')
+ :param default: The default value for this option
+ :param label: A short (few words) description of this option
+ :param desc: A longer description of this option
+ :param choices: A list of possible values, used only if type='choices'
+ '''
+ self.name, self.type, self.default, self.label, self.desc = (name,
+ type_, default, label, desc)
+ self.choices = choices
class Source(Plugin):
@@ -158,10 +173,14 @@ class Source(Plugin):
supports_gzip_transfer_encoding = False
#: Cached cover URLs can sometimes be unreliable (i.e. the download could
- #: fail or the returned image could be bogus. If that is the case set this to
- #: False
+ #: fail or the returned image could be bogus. If that is often the case
+ #: with this source set to False
cached_cover_url_is_reliable = True
+ #: A list of :class:`Option` objects. They will be used to automatically
+ #: construct the configuration widget for this plugin
+ options = ()
+
def __init__(self, *args, **kwargs):
Plugin.__init__(self, *args, **kwargs)
@@ -170,6 +189,9 @@ class Source(Plugin):
self.cache_lock = threading.RLock()
self._config_obj = None
self._browser = None
+ self.prefs.defaults['ignore_fields'] = []
+ for opt in self.options:
+ self.prefs.defaults[opt.name] = opt.default
# Configuration {{{
@@ -180,6 +202,16 @@ class Source(Plugin):
'''
return True
+ def is_customizable(self):
+ return True
+
+ def config_widget(self):
+ from calibre.gui2.metadata.config import ConfigWidget
+ return ConfigWidget(self)
+
+ def save_settings(self, config_widget):
+ config_widget.commit()
+
@property
def prefs(self):
if self._config_obj is None:
diff --git a/src/calibre/ebooks/metadata/sources/openlibrary.py b/src/calibre/ebooks/metadata/sources/openlibrary.py
index 19b8747265..4645d2a18a 100644
--- a/src/calibre/ebooks/metadata/sources/openlibrary.py
+++ b/src/calibre/ebooks/metadata/sources/openlibrary.py
@@ -12,7 +12,7 @@ from calibre.ebooks.metadata.sources.base import Source
class OpenLibrary(Source):
name = 'Open Library'
- description = _('Downloads metadata from The Open Library')
+ description = _('Downloads covers from The Open Library')
capabilities = frozenset(['cover'])
diff --git a/src/calibre/gui2/preferences/metadata_sources.py b/src/calibre/gui2/preferences/metadata_sources.py
index 8324684bc8..4500a03b30 100644
--- a/src/calibre/gui2/preferences/metadata_sources.py
+++ b/src/calibre/gui2/preferences/metadata_sources.py
@@ -9,14 +9,15 @@ __docformat__ = 'restructuredtext en'
from operator import attrgetter
-from PyQt4.Qt import (QAbstractTableModel, Qt, QAbstractListModel)
+from PyQt4.Qt import (QAbstractTableModel, Qt, QAbstractListModel, QWidget,
+ pyqtSignal, QVBoxLayout, QDialogButtonBox, QFrame, QLabel)
from calibre.gui2.preferences import ConfigWidgetBase, test_widget
from calibre.gui2.preferences.metadata_sources_ui import Ui_Form
from calibre.ebooks.metadata.sources.base import msprefs
from calibre.customize.ui import (all_metadata_plugins, is_disabled,
enable_plugin, disable_plugin, default_disabled_plugins)
-from calibre.gui2 import NONE
+from calibre.gui2 import NONE, error_dialog
class SourcesModel(QAbstractTableModel): # {{{
@@ -64,7 +65,8 @@ class SourcesModel(QAbstractTableModel): # {{{
elif role == Qt.CheckStateRole and col == 0:
orig = Qt.Unchecked if is_disabled(plugin) else Qt.Checked
return self.enabled_overrides.get(plugin, orig)
-
+ elif role == Qt.UserRole:
+ return plugin
return NONE
def setData(self, index, val, role):
@@ -127,6 +129,7 @@ class SourcesModel(QAbstractTableModel): # {{{
class FieldsModel(QAbstractListModel): # {{{
+
def __init__(self, parent=None):
QAbstractTableModel.__init__(self, parent)
@@ -143,6 +146,7 @@ class FieldsModel(QAbstractListModel): # {{{
'language': _('Language'),
}
self.overrides = {}
+ self.exclude = frozenset(['series_index'])
def rowCount(self, parent=None):
return len(self.fields)
@@ -153,7 +157,7 @@ class FieldsModel(QAbstractListModel): # {{{
fields |= p.touched_fields
self.fields = []
for x in fields:
- if not x.startswith('identifier:') and x not in ('series_index',):
+ if not x.startswith('identifier:') and x not in self.exclude:
self.fields.append(x)
self.fields.sort(key=lambda x:self.descs.get(x, x))
self.reset()
@@ -204,6 +208,41 @@ class FieldsModel(QAbstractListModel): # {{{
# }}}
+class PluginConfig(QWidget): # {{{
+
+ finished = pyqtSignal()
+
+ def __init__(self, plugin, parent):
+ QWidget.__init__(self, parent)
+
+ self.plugin = plugin
+
+ self.l = l = QVBoxLayout()
+ self.setLayout(l)
+ self.c = c = QLabel(_('Configure %s
%s') % (plugin.name,
+ plugin.description))
+ c.setAlignment(Qt.AlignHCenter)
+ l.addWidget(c)
+
+ self.config_widget = plugin.config_widget()
+ self.l.addWidget(self.config_widget)
+
+ self.bb = QDialogButtonBox(
+ QDialogButtonBox.Save|QDialogButtonBox.Cancel,
+ parent=self)
+ self.bb.accepted.connect(self.finished)
+ self.bb.rejected.connect(self.finished)
+ self.bb.accepted.connect(self.commit)
+ l.addWidget(self.bb)
+
+ self.f = QFrame(self)
+ self.f.setFrameShape(QFrame.HLine)
+ l.addWidget(self.f)
+
+ def commit(self):
+ self.plugin.save_settings(self.config_widget)
+# }}}
+
class ConfigWidget(ConfigWidgetBase, Ui_Form):
def genesis(self, gui):
@@ -223,7 +262,27 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.fields_model.dataChanged.connect(self.changed_signal)
def configure_plugin(self):
- pass
+ for index in self.sources_view.selectionModel().selectedRows():
+ plugin = self.sources_model.data(index, Qt.UserRole)
+ if plugin is not NONE:
+ return self.do_config(plugin)
+ error_dialog(self, _('No source selected'),
+ _('No source selected, cannot configure.'), show=True)
+
+ def do_config(self, plugin):
+ self.pc = PluginConfig(plugin, self)
+ self.stack.insertWidget(1, self.pc)
+ self.stack.setCurrentIndex(1)
+ self.pc.finished.connect(self.pc_finished)
+
+ def pc_finished(self):
+ try:
+ self.pc.finished.diconnect()
+ except:
+ pass
+ self.stack.setCurrentIndex(0)
+ self.stack.removeWidget(self.pc)
+ self.pc = None
def initialize(self):
ConfigWidgetBase.initialize(self)