From 77f0ce829293f27149304056320fd435485131de Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 4 Sep 2010 08:48:17 -0600 Subject: [PATCH] Port the conversion preferences widgets to the new scheme --- src/calibre/customize/__init__.py | 9 +- src/calibre/customize/builtins.py | 30 +++++- src/calibre/gui2/convert/__init__.py | 44 +++++++- src/calibre/gui2/convert/page_setup.py | 12 ++- src/calibre/gui2/preferences/__init__.py | 23 ++++- src/calibre/gui2/preferences/conversion.py | 114 +++++++++++++++++++++ src/calibre/gui2/preferences/conversion.ui | 82 +++++++++++++++ 7 files changed, 304 insertions(+), 10 deletions(-) create mode 100644 src/calibre/gui2/preferences/conversion.py create mode 100644 src/calibre/gui2/preferences/conversion.ui 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 @@ class PreferencesPlugin(Plugin): # {{{ 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 @@ __copyright__ = '2009, Kovid Goyal ' __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 @@ class Widget(QWidget): 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 @@ class Widget(QWidget): 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 @@ class Widget(QWidget): 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 @@ class Widget(QWidget): 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 @@ class PageSetupWidget(Widget, Ui_Form): 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 @@ class PageSetupWidget(Widget, Ui_Form): 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 PyQt4.Qt import QWidget, pyqtSignal, QCheckBox, QAbstractSpinBox, \ from calibre.customize.ui import preferences_plugins +class AbortCommit(Exception): + pass + class ConfigWidgetInterface(object): changed_signal = None @@ -24,7 +27,13 @@ class ConfigWidgetInterface(object): 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 ' +__docformat__ = 'restructuredtext en' + +from PyQt4.Qt import QIcon, Qt, QStringListModel, QVariant + +from calibre.gui2.preferences import ConfigWidgetBase, test_widget, AbortCommit +from calibre.ebooks.conversion.plumber import Plumber +from calibre.utils.logging import Log +from calibre.gui2.preferences.conversion_ui import Ui_Form +from calibre.gui2.convert.look_and_feel import LookAndFeelWidget +from calibre.gui2.convert.page_setup import PageSetupWidget +from calibre.gui2.convert.structure_detection import StructureDetectionWidget +from calibre.gui2.convert.toc import TOCWidget +from calibre.customize.ui import input_format_plugins, output_format_plugins +from calibre.gui2.convert import config_widget_for_input_plugin + +class Model(QStringListModel): + + def __init__(self, widgets): + QStringListModel.__init__(self) + self.widgets = widgets + self.setStringList([w.TITLE for w in widgets]) + + def data(self, index, role): + if role == Qt.DecorationRole: + w = self.widgets[index.row()] + if w.ICON: + return QVariant(QIcon(w.ICON)) + return QStringListModel.data(self, index, role) + +class Base(ConfigWidgetBase, Ui_Form): + + HAS_ICONS = False + + def genesis(self, gui): + log = Log() + log.outputs = [] + + self.plumber = Plumber('dummy.epub', 'dummy.epub', log, dummy=True, + merge_plugin_recs=False) + + def widget_factory(cls): + return cls(self, self.plumber.get_option_by_name, + self.plumber.get_option_help, None, None) + + self.load_conversion_widgets() + widgets = list(map(widget_factory, self.conversion_widgets)) + self.model = Model(widgets) + self.list.setModel(self.model) + + for w in widgets: + w.changed_signal.connect(self.changed_signal) + self.stack.addWidget(w) + + + #self.list.currentRowChanged.connect(self.stack.setCurrentIndex) + #self.list.setCurrentRow(0) + + + def initialize(self): + ConfigWidgetBase.initialize(self) + + def restore_defaults(self): + ConfigWidgetBase.restore_defaults(self) + self.stack.currentWidget().restore_defaults(self.plumber.get_option_by_name) + self.changed_signal.emit() + + def commit(self): + for widget in self.conversion_widgets: + if not widget.pre_commit_check(): + raise AbortCommit('abort') + widget.commit(save_defaults=True) + return ConfigWidgetBase.commit(self) + +class CommonOptions(Base): + + HAS_ICONS = True + + def load_conversion_widgets(self): + self.conversion_widgets = [LookAndFeelWidget, PageSetupWidget, + StructureDetectionWidget, TOCWidget] + +class InputOptions(Base): + + def load_conversion_widgets(self): + self.conversion_widgets = [] + for plugin in input_format_plugins(): + pw = config_widget_for_input_plugin(plugin) + if pw is not None: + self.conversion_widgets.append(pw) + +class OutputOptions(Base): + + def load_conversion_widgets(self): + self.conversion_widgets = [] + for plugin in output_format_plugins(): + name = plugin.name.lower().replace(' ', '_') + try: + output_widget = __import__('calibre.gui2.convert.'+name, + fromlist=[1]) + pw = output_widget.PluginWidget + self.conversion_widgets.append(pw) + except ImportError: + continue + +if __name__ == '__main__': + from PyQt4.Qt import QApplication + app = QApplication([]) + test_widget('Conversion', 'Common Options') + diff --git a/src/calibre/gui2/preferences/conversion.ui b/src/calibre/gui2/preferences/conversion.ui new file mode 100644 index 0000000000..2f4f042805 --- /dev/null +++ b/src/calibre/gui2/preferences/conversion.ui @@ -0,0 +1,82 @@ + + + Form + + + + 0 + 0 + 720 + 603 + + + + Form + + + + + + + 0 + 0 + + + + + 180 + 0 + + + + + 180 + 16777215 + + + + + 75 + true + + + + QAbstractItemView::NoEditTriggers + + + true + + + false + + + + 48 + 48 + + + + QAbstractItemView::ScrollPerItem + + + QAbstractItemView::ScrollPerPixel + + + QListView::TopToBottom + + + 20 + + + QListView::ListMode + + + + + + + + + + +