diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index 27e319de14..91052bbc2b 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -404,9 +404,12 @@ def create_widget(self, parent=None): The default implementation uses :attr:`config_widget` to instantiate the widget. ''' - base = __import__(self.config_widget, fromlist=[1]) - widget = base.ConfigWidget(parent) - return widget + base, _, wc = self.config_widget.partition(':') + if not wc: + wc = 'ConfigWidget' + base = __import__(base, fromlist=[1]) + widget = getattr(base, wc) + return widget(parent) # }}} diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index d1e08e206e..8912df9db4 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -712,7 +712,35 @@ class Toolbar(PreferencesPlugin): name_order = 4 config_widget = 'calibre.gui2.preferences.toolbar' -plugins += [LookAndFeel, Behavior, Columns, Toolbar] +class InputOptions(PreferencesPlugin): + name = 'Input Options' + gui_name = _('Input Options') + category = 'Conversion' + gui_category = _('Conversion') + category_order = 2 + name_order = 1 + config_widget = 'calibre.gui2.preferences.conversion:InputOptions' + +class CommonOptions(PreferencesPlugin): + name = 'Common Options' + gui_name = _('Common Options') + category = 'Conversion' + gui_category = _('Conversion') + category_order = 2 + name_order = 2 + config_widget = 'calibre.gui2.preferences.conversion:CommonOptions' + +class OutputOptions(PreferencesPlugin): + name = 'Output Options' + gui_name = _('Output Options') + category = 'Conversion' + gui_category = _('Conversion') + category_order = 2 + name_order = 3 + config_widget = 'calibre.gui2.preferences.conversion:OutputOptions' + +plugins += [LookAndFeel, Behavior, Columns, Toolbar, InputOptions, + CommonOptions, OutputOptions] #}}} diff --git a/src/calibre/gui2/convert/__init__.py b/src/calibre/gui2/convert/__init__.py index c8f8733899..ac6d3f1513 100644 --- a/src/calibre/gui2/convert/__init__.py +++ b/src/calibre/gui2/convert/__init__.py @@ -7,9 +7,10 @@ __docformat__ = 'restructuredtext en' import textwrap +from functools import partial from PyQt4.Qt import QWidget, QSpinBox, QDoubleSpinBox, QLineEdit, QTextEdit, \ - QCheckBox, QComboBox, Qt, QIcon, SIGNAL + QCheckBox, QComboBox, Qt, QIcon, pyqtSignal from calibre.customize.conversion import OptionRecommendation from calibre.ebooks.conversion.config import load_defaults, \ @@ -43,6 +44,9 @@ class Widget(QWidget): HELP = '' COMMIT_NAME = None + changed_signal = pyqtSignal() + set_help = pyqtSignal(object) + def __init__(self, parent, options): QWidget.__init__(self, parent) self.setupUi(self) @@ -54,6 +58,7 @@ def __init__(self, parent, options): if not hasattr(self, 'opt_'+name): raise Exception('Option %s missing in %s'%(name, self.__class__.__name__)) + self.connect_gui_obj(getattr(self, 'opt_'+name)) def initialize_options(self, get_option, get_help, db=None, book_id=None): ''' @@ -76,6 +81,12 @@ def initialize_options(self, get_option, get_help, db=None, book_id=None): self.apply_recommendations(defaults) self.setup_help(get_help) + def restore_defaults(self, get_option): + defaults = GuiRecommendations() + defaults.merge_recommendations(get_option, OptionRecommendation.LOW, + self._options) + self.apply_recommendations(defaults) + def commit_options(self, save_defaults=False): recs = self.create_recommendations() if save_defaults: @@ -124,6 +135,35 @@ def get_value(self, g): else: raise Exception('Can\'t get value from %s'%type(g)) + def gui_obj_changed(self, gui_obj, *args): + self.changed_signal.emit() + + def connect_gui_obj(self, g): + f = partial(self.gui_obj_changed, g) + try: + self.connect_gui_obj_handler(g, f) + return + except NotImplementedError: + pass + from calibre.gui2.convert.xpath_wizard import XPathEdit + from calibre.gui2.convert.regex_builder import RegexEdit + if isinstance(g, (QSpinBox, QDoubleSpinBox)): + g.valueChanged.connect(f) + elif isinstance(g, (QLineEdit, QTextEdit)): + g.textChanged.connect(f) + elif isinstance(g, QComboBox): + g.editTextChanged.connect(f) + g.currentIndexChanged.connect(f) + elif isinstance(g, QCheckBox): + g.stateChanged.connect(f) + elif isinstance(g, (XPathEdit, RegexEdit)): + g.edit.editTextChanged.connect(f) + g.edit.currentIndexChanged.connect(f) + else: + raise Exception('Can\'t connect %s'%type(g)) + + def connect_gui_obj_handler(self, gui_obj, slot): + raise NotImplementedError() def set_value(self, g, val): from calibre.gui2.convert.xpath_wizard import XPathEdit @@ -154,7 +194,7 @@ def set_value(self, g, val): def set_help(self, msg): if msg and getattr(msg, 'strip', lambda:True)(): try: - self.emit(SIGNAL('set_help(PyQt_PyObject)'), msg) + self.set_help.emit(msg) except: pass diff --git a/src/calibre/gui2/convert/page_setup.py b/src/calibre/gui2/convert/page_setup.py index e824c18b57..a0ca16297c 100644 --- a/src/calibre/gui2/convert/page_setup.py +++ b/src/calibre/gui2/convert/page_setup.py @@ -36,6 +36,7 @@ class PageSetupWidget(Widget, Ui_Form): COMMIT_NAME = 'page_setup' def __init__(self, parent, get_option, get_help, db=None, book_id=None): + self.__connections = [] Widget.__init__(self, parent, ['margin_top', 'margin_left', 'margin_right', 'margin_bottom', 'input_profile', 'output_profile'] @@ -46,6 +47,10 @@ def __init__(self, parent, get_option, get_help, db=None, book_id=None): self.output_model = ProfileModel(output_profiles()) self.opt_input_profile.setModel(self.input_model) self.opt_output_profile.setModel(self.output_model) + for g, slot in self.__connections: + g.selectionModel().currentChanged.connect(slot) + del self.__connections + for x in (self.opt_input_profile, self.opt_output_profile): x.setMouseTracking(True) self.connect(x, SIGNAL('entered(QModelIndex)'), self.show_desc) @@ -55,12 +60,15 @@ def __init__(self, parent, get_option, get_help, db=None, book_id=None): it = unicode(self.opt_output_profile.toolTip()) self.opt_output_profile.setToolTip('
'+it.replace('t.','ce.\n
'))
-
-
def show_desc(self, index):
desc = index.model().data(index, Qt.StatusTipRole).toString()
self.profile_description.setText(desc)
+ def connect_gui_obj_handler(self, g, slot):
+ if g not in (self.opt_input_profile, self.opt_output_profile):
+ raise NotImplementedError()
+ self.__connections.append((g, slot))
+
def set_value_handler(self, g, val):
if g in (self.opt_input_profile, self.opt_output_profile):
g.clearSelection()
diff --git a/src/calibre/gui2/preferences/__init__.py b/src/calibre/gui2/preferences/__init__.py
index 5d46ee2eb9..fd26344a2e 100644
--- a/src/calibre/gui2/preferences/__init__.py
+++ b/src/calibre/gui2/preferences/__init__.py
@@ -10,6 +10,9 @@
from calibre.customize.ui import preferences_plugins
+class AbortCommit(Exception):
+ pass
+
class ConfigWidgetInterface(object):
changed_signal = None
@@ -24,7 +27,13 @@ def restore_defaults(self):
pass
def commit(self):
- pass
+ '''
+ Save any changed settings. Return True if the changes require a
+ restart, False otherwise. Raise an :class:`AbortCommit` exception
+ to indicate that an error occurred. You are responsible for giving the
+ suer feedback about what the error is and how to correct it.
+ '''
+ return False
class Setting(object):
@@ -184,8 +193,17 @@ def get_plugin(category, name):
def test_widget(category, name, gui=None): # {{{
from PyQt4.Qt import QDialog, QVBoxLayout, QDialogButtonBox
+ class Dialog(QDialog):
+ def set_widget(self, w): self.w = w
+ def accept(self):
+ try:
+ self.w.commit()
+ except AbortCommit:
+ return
+ QDialog.accept(self)
+
pl = get_plugin(category, name)
- d = QDialog()
+ d = Dialog()
d.resize(750, 550)
d.setWindowTitle(category + " - " + name)
bb = QDialogButtonBox(d)
@@ -193,6 +211,7 @@ def test_widget(category, name, gui=None): # {{{
bb.accepted.connect(d.accept)
bb.rejected.connect(d.reject)
w = pl.create_widget(d)
+ d.set_widget(w)
bb.button(bb.RestoreDefaults).clicked.connect(w.restore_defaults)
bb.button(bb.Apply).setEnabled(False)
bb.button(bb.Apply).clicked.connect(d.accept)
diff --git a/src/calibre/gui2/preferences/conversion.py b/src/calibre/gui2/preferences/conversion.py
new file mode 100644
index 0000000000..d9c206bd80
--- /dev/null
+++ b/src/calibre/gui2/preferences/conversion.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
+
+__license__ = 'GPL v3'
+__copyright__ = '2010, Kovid Goyal