Port the conversion preferences widgets to the new scheme

This commit is contained in:
Kovid Goyal 2010-09-04 08:48:17 -06:00
parent e7f2051c5a
commit 77f0ce8292
7 changed files with 304 additions and 10 deletions

View File

@ -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)
# }}}

View File

@ -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]
#}}}

View File

@ -7,9 +7,10 @@ __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__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

View File

@ -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('<p>'+it.replace('t.','ce.\n<br>'))
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()

View File

@ -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)

View File

@ -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 <kovid@kovidgoyal.net>'
__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')

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>720</width>
<height>603</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QListView" name="list">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>180</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>180</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="tabKeyNavigation">
<bool>true</bool>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerItem</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="flow">
<enum>QListView::TopToBottom</enum>
</property>
<property name="spacing">
<number>20</number>
</property>
<property name="viewMode">
<enum>QListView::ListMode</enum>
</property>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="stack"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>