From 9f8ce9dc89d2b5f292234b9d652d6d36579e0999 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 4 Sep 2010 16:00:51 -0600 Subject: [PATCH] Preferences widget for saving to disk --- src/calibre/customize/builtins.py | 11 +- src/calibre/gui2/preferences/__init__.py | 60 ++++++++-- src/calibre/gui2/preferences/conversion.py | 4 +- src/calibre/gui2/preferences/history.py | 49 ++++++++ src/calibre/gui2/preferences/save_template.py | 68 +++++++++++ src/calibre/gui2/preferences/save_template.ui | 60 ++++++++++ src/calibre/gui2/preferences/saving.py | 52 +++++++++ src/calibre/gui2/preferences/saving.ui | 110 ++++++++++++++++++ src/calibre/library/save_to_disk.py | 2 +- src/calibre/utils/config.py | 3 + 10 files changed, 403 insertions(+), 16 deletions(-) create mode 100644 src/calibre/gui2/preferences/history.py create mode 100644 src/calibre/gui2/preferences/save_template.py create mode 100644 src/calibre/gui2/preferences/save_template.ui create mode 100644 src/calibre/gui2/preferences/saving.py create mode 100644 src/calibre/gui2/preferences/saving.ui diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 732c5dbc76..9d7f9480d9 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -748,8 +748,17 @@ class Adding(PreferencesPlugin): name_order = 1 config_widget = 'calibre.gui2.preferences.adding' +class Saving(PreferencesPlugin): + name = 'Saving' + gui_name = _('Saving books to disk') + category = 'Import/Export' + gui_category = _('Import/Export') + category_order = 3 + name_order = 2 + config_widget = 'calibre.gui2.preferences.saving' + plugins += [LookAndFeel, Behavior, Columns, Toolbar, InputOptions, - CommonOptions, OutputOptions, Adding] + CommonOptions, OutputOptions, Adding, Saving] #}}} diff --git a/src/calibre/gui2/preferences/__init__.py b/src/calibre/gui2/preferences/__init__.py index fd26344a2e..ab4ffe3f3b 100644 --- a/src/calibre/gui2/preferences/__init__.py +++ b/src/calibre/gui2/preferences/__init__.py @@ -5,10 +5,13 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' +import textwrap + from PyQt4.Qt import QWidget, pyqtSignal, QCheckBox, QAbstractSpinBox, \ QLineEdit, QComboBox, QVariant from calibre.customize.ui import preferences_plugins +from calibre.utils.config import ConfigProxy class AbortCommit(Exception): pass @@ -65,6 +68,20 @@ class Setting(object): else: raise ValueError('Unknown data type') + if isinstance(self.config_obj, ConfigProxy) and \ + not unicode(self.gui_obj.toolTip()): + h = self.config_obj.help(self.name) + if h: + self.gui_obj.setToolTip(h) + tt = unicode(self.gui_obj.toolTip()) + if tt: + if not unicode(self.gui_obj.whatsThis()): + self.gui_obj.setWhatsThis(tt) + if not unicode(self.gui_obj.statusTip()): + self.gui_obj.setStatusTip(tt) + tt = '\n'.join(textwrap.wrap(tt, 70)) + self.gui_obj.setToolTip(tt) + def changed(self, *args): self.widget.changed_signal.emit() @@ -120,7 +137,7 @@ class Setting(object): elif self.datatype == 'number': val = self.gui_obj.value() elif self.datatype == 'string': - val = unicode(self.gui_name.text()).strip() + val = unicode(self.gui_obj.text()).strip() if self.empty_string_is_None and not val: val = None elif self.datatype == 'choice': @@ -182,7 +199,6 @@ class ConfigWidgetBase(QWidget, ConfigWidgetInterface): setting.restore_defaults() - def get_plugin(category, name): for plugin in preferences_plugins(): if plugin.category == category and plugin.name == name: @@ -191,7 +207,21 @@ def get_plugin(category, name): 'No Preferences Plugin with category: %s and name: %s found' % (category, name)) -def test_widget(category, name, gui=None): # {{{ +# Testing {{{ + +def init_gui(): + from calibre.gui2.ui import Main + from calibre.gui2.main import option_parser + from calibre.library 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, show_gui=False) + return gui + +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 @@ -222,15 +252,8 @@ def test_widget(category, name, gui=None): # {{{ l.addWidget(bb) mygui = gui is None if gui is None: - from calibre.gui2.ui import Main - from calibre.gui2.main import option_parser - from calibre.library 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, show_gui=False) + gui = init_gui() + mygui = True w.genesis(gui) w.initialize() restart_required = False @@ -241,5 +264,18 @@ def test_widget(category, name, gui=None): # {{{ warning_dialog(gui, 'Restart required', 'Restart required', show=True) if mygui: gui.shutdown() + +def test_all(): + from PyQt4.Qt import QApplication + app = QApplication([]) + app + gui = init_gui() + for plugin in preferences_plugins(): + test_widget(plugin.category, plugin.name, gui=gui) + gui.shutdown() + +if __name__ == '__main__': + test_all() # }}} + diff --git a/src/calibre/gui2/preferences/conversion.py b/src/calibre/gui2/preferences/conversion.py index 2950f58c02..82d6f916b8 100644 --- a/src/calibre/gui2/preferences/conversion.py +++ b/src/calibre/gui2/preferences/conversion.py @@ -108,6 +108,6 @@ if __name__ == '__main__': from PyQt4.Qt import QApplication app = QApplication([]) #test_widget('Conversion', 'Input Options') - #test_widget('Conversion', 'Common Options') - test_widget('Conversion', 'Output Options') + test_widget('Conversion', 'Common Options') + #test_widget('Conversion', 'Output Options') diff --git a/src/calibre/gui2/preferences/history.py b/src/calibre/gui2/preferences/history.py new file mode 100644 index 0000000000..c1c68642f6 --- /dev/null +++ b/src/calibre/gui2/preferences/history.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import with_statement + +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import textwrap + +from PyQt4.Qt import QComboBox, QStringList, Qt + +from calibre.gui2 import config as gui_conf + +class HistoryBox(QComboBox): + + def __init__(self, parent=None): + QComboBox.__init__(self, parent) + self.setEditable(True) + + def initialize(self, opt_name, default, help=None): + self.opt_name = opt_name + self.set_value(default) + if help: + self.setStatusTip(help) + help = '\n'.join(textwrap.wrap(help)) + self.setToolTip(help) + self.setWhatsThis(help) + + def set_value(self, val): + history = gui_conf[self.opt_name] + if val not in history: + history.append(val) + self.clear() + self.addItems(QStringList(history)) + self.setCurrentIndex(self.findText(val, Qt.MatchFixedString)) + + def save_history(self, opt_name): + history = [unicode(self.itemText(i)) for i in range(self.count())] + ct = self.text() + if ct not in history: + history = [ct] + history + gui_conf[opt_name] = history[:10] + + def text(self): + return unicode(self.currentText()).strip() + + + diff --git a/src/calibre/gui2/preferences/save_template.py b/src/calibre/gui2/preferences/save_template.py new file mode 100644 index 0000000000..d325ac42ff --- /dev/null +++ b/src/calibre/gui2/preferences/save_template.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import with_statement + +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +from PyQt4.Qt import QWidget, pyqtSignal + +from calibre.gui2 import error_dialog +from calibre.gui2.preferences.save_template_ui import Ui_Form +from calibre.library.save_to_disk import FORMAT_ARG_DESCS, \ + preprocess_template + +class SaveTemplate(QWidget, Ui_Form): + + changed_signal = pyqtSignal() + + def __init__(self, *args): + QWidget.__init__(self, *args) + Ui_Form.__init__(self) + self.setupUi(self) + + def initialize(self, name, default, help): + variables = sorted(FORMAT_ARG_DESCS.keys()) + rows = [] + for var in variables: + rows.append(u'%s%s'% + (var, FORMAT_ARG_DESCS[var])) + table = u'%s
'%(u'\n'.join(rows)) + self.template_variables.setText(table) + + self.opt_template.initialize(name+'_template_history', + default, help) + self.opt_template.editTextChanged.connect(self.changed) + self.opt_template.currentIndexChanged.connect(self.changed) + self.option_name = name + + def changed(self, *args): + self.changed_signal.emit() + + def validate(self): + tmpl = preprocess_template(self.opt_template.text()) + fa = {} + for x in FORMAT_ARG_DESCS.keys(): + fa[x]='random long string' + try: + tmpl.format(**fa) + except Exception, err: + error_dialog(self, _('Invalid template'), + '

'+_('The template %s is invalid:')%tmpl + \ + '
'+str(err), show=True) + return False + return True + + def set_value(self, val): + self.opt_template.set_value(val) + + def save_settings(self, config, name): + val = unicode(self.opt_template.text()) + config.set(name, val) + self.opt_template.save_history(self.option_name+'_template_history') + + + + + diff --git a/src/calibre/gui2/preferences/save_template.ui b/src/calibre/gui2/preferences/save_template.ui new file mode 100644 index 0000000000..0d7422abcf --- /dev/null +++ b/src/calibre/gui2/preferences/save_template.ui @@ -0,0 +1,60 @@ + + + Form + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + Save &template + + + + + + By adjusting the template below, you can control what folders the files are saved in and what filenames they are given. You can use the / character to indicate sub-folders. Available metadata variables are described below. If a particular book does not have some metadata, the variable will be replaced by the empty string. + + + true + + + + + + + Available variables: + + + + + + + + + + + + + + + + + HistoryBox + QComboBox +

calibre/gui2/preferences/history.h
+ + + + + diff --git a/src/calibre/gui2/preferences/saving.py b/src/calibre/gui2/preferences/saving.py new file mode 100644 index 0000000000..a698083475 --- /dev/null +++ b/src/calibre/gui2/preferences/saving.py @@ -0,0 +1,52 @@ +#!/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 calibre.gui2.preferences import ConfigWidgetBase, test_widget, \ + AbortCommit +from calibre.gui2.preferences.saving_ui import Ui_Form +from calibre.utils.config import ConfigProxy +from calibre.library.save_to_disk import config + +class ConfigWidget(ConfigWidgetBase, Ui_Form): + + def genesis(self, gui): + self.gui = gui + self.proxy = ConfigProxy(config()) + + r = self.register + + for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf', + 'replace_whitespace', 'to_lowercase', 'formats', 'timefmt'): + r(x, self.proxy) + + self.save_template.changed_signal.connect(self.changed_signal.emit) + + + def initialize(self): + ConfigWidgetBase.initialize(self) + self.save_template.blockSignals(True) + self.save_template.initialize('save_to_disk', self.proxy['template'], + self.proxy.help('template')) + self.save_template.blockSignals(False) + + def restore_defaults(self): + ConfigWidgetBase.restore_defaults(self) + self.save_template.set_value(self.proxy.defaults['template']) + + def commit(self): + if not self.save_template.validate(): + raise AbortCommit('abort') + self.save_template.save_settings(self.proxy, 'template') + return ConfigWidgetBase.commit(self) + +if __name__ == '__main__': + from PyQt4.Qt import QApplication + app = QApplication([]) + test_widget('Import/Export', 'Saving') + diff --git a/src/calibre/gui2/preferences/saving.ui b/src/calibre/gui2/preferences/saving.ui new file mode 100644 index 0000000000..e4f5aaeb47 --- /dev/null +++ b/src/calibre/gui2/preferences/saving.ui @@ -0,0 +1,110 @@ + + + Form + + + + 0 + 0 + 707 + 340 + + + + Form + + + + + + Here you can control how calibre will save your books when you click the Save to Disk button: + + + true + + + + + + + Save &cover separately + + + + + + + Replace space with &underscores + + + + + + + Update &metadata in saved copies + + + + + + + Change paths to &lowercase + + + + + + + Format &dates as: + + + opt_timefmt + + + + + + + + + + File &formats to save: + + + opt_formats + + + + + + + + + + + + + Convert non-English characters to &English equivalents + + + + + + + Save metadata in &OPF file + + + + + + + + SaveTemplate + QWidget +
calibre/gui2/preferences/save_template.h
+ 1 +
+
+ + +
diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py index 15020855f7..f5c4063789 100644 --- a/src/calibre/library/save_to_disk.py +++ b/src/calibre/library/save_to_disk.py @@ -61,7 +61,7 @@ def config(defaults=None): 'actual e-book file(s).')) x('formats', default='all', help=_('Comma separated list of formats to save for each book.' - ' By default all available books are saved.')) + ' By default all available formats are saved.')) x('template', default=DEFAULT_TEMPLATE, help=_('The template to control the filename and directory structure of the saved files. ' 'Default is "%s" which will save books into a per-author ' diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py index 695ece3c48..11c58f7769 100644 --- a/src/calibre/utils/config.py +++ b/src/calibre/utils/config.py @@ -490,6 +490,9 @@ class ConfigProxy(object): setattr(self.__opts, key, val) return self.__config.set(key, val) + def help(self, key): + return self.__config.get_option(key).help + class DynamicConfig(dict): ''' A replacement for QSettings that supports dynamic config keys.