Preferences widget for saving to disk

This commit is contained in:
Kovid Goyal 2010-09-04 16:00:51 -06:00
parent d223636fd4
commit 9f8ce9dc89
10 changed files with 403 additions and 16 deletions

View File

@ -748,8 +748,17 @@ class Adding(PreferencesPlugin):
name_order = 1 name_order = 1
config_widget = 'calibre.gui2.preferences.adding' 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, plugins += [LookAndFeel, Behavior, Columns, Toolbar, InputOptions,
CommonOptions, OutputOptions, Adding] CommonOptions, OutputOptions, Adding, Saving]
#}}} #}}}

View File

@ -5,10 +5,13 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import textwrap
from PyQt4.Qt import QWidget, pyqtSignal, QCheckBox, QAbstractSpinBox, \ from PyQt4.Qt import QWidget, pyqtSignal, QCheckBox, QAbstractSpinBox, \
QLineEdit, QComboBox, QVariant QLineEdit, QComboBox, QVariant
from calibre.customize.ui import preferences_plugins from calibre.customize.ui import preferences_plugins
from calibre.utils.config import ConfigProxy
class AbortCommit(Exception): class AbortCommit(Exception):
pass pass
@ -65,6 +68,20 @@ class Setting(object):
else: else:
raise ValueError('Unknown data type') 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): def changed(self, *args):
self.widget.changed_signal.emit() self.widget.changed_signal.emit()
@ -120,7 +137,7 @@ class Setting(object):
elif self.datatype == 'number': elif self.datatype == 'number':
val = self.gui_obj.value() val = self.gui_obj.value()
elif self.datatype == 'string': 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: if self.empty_string_is_None and not val:
val = None val = None
elif self.datatype == 'choice': elif self.datatype == 'choice':
@ -182,7 +199,6 @@ class ConfigWidgetBase(QWidget, ConfigWidgetInterface):
setting.restore_defaults() setting.restore_defaults()
def get_plugin(category, name): def get_plugin(category, name):
for plugin in preferences_plugins(): for plugin in preferences_plugins():
if plugin.category == category and plugin.name == name: 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' % 'No Preferences Plugin with category: %s and name: %s found' %
(category, name)) (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 from PyQt4.Qt import QDialog, QVBoxLayout, QDialogButtonBox
class Dialog(QDialog): class Dialog(QDialog):
def set_widget(self, w): self.w = w def set_widget(self, w): self.w = w
@ -222,15 +252,8 @@ def test_widget(category, name, gui=None): # {{{
l.addWidget(bb) l.addWidget(bb)
mygui = gui is None mygui = gui is None
if gui is None: if gui is None:
from calibre.gui2.ui import Main gui = init_gui()
from calibre.gui2.main import option_parser mygui = True
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)
w.genesis(gui) w.genesis(gui)
w.initialize() w.initialize()
restart_required = False restart_required = False
@ -241,5 +264,18 @@ def test_widget(category, name, gui=None): # {{{
warning_dialog(gui, 'Restart required', 'Restart required', show=True) warning_dialog(gui, 'Restart required', 'Restart required', show=True)
if mygui: if mygui:
gui.shutdown() 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()
# }}} # }}}

View File

@ -108,6 +108,6 @@ if __name__ == '__main__':
from PyQt4.Qt import QApplication from PyQt4.Qt import QApplication
app = QApplication([]) app = QApplication([])
#test_widget('Conversion', 'Input Options') #test_widget('Conversion', 'Input Options')
#test_widget('Conversion', 'Common Options') test_widget('Conversion', 'Common Options')
test_widget('Conversion', 'Output Options') #test_widget('Conversion', 'Output Options')

View File

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

View File

@ -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 <kovid@kovidgoyal.net>'
__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'<tr><td>%s</td><td>%s</td></tr>'%
(var, FORMAT_ARG_DESCS[var]))
table = u'<table>%s</table>'%(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'),
'<p>'+_('The template %s is invalid:')%tmpl + \
'<br>'+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')

View File

@ -0,0 +1,60 @@
<?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>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Save &amp;template</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>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.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Available variables:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QTextBrowser" name="template_variables"/>
</item>
<item row="1" column="0">
<widget class="HistoryBox" name="opt_template"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>HistoryBox</class>
<extends>QComboBox</extends>
<header>calibre/gui2/preferences/history.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

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

View File

@ -0,0 +1,110 @@
<?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>707</width>
<height>340</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Here you can control how calibre will save your books when you click the Save to Disk button:</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="opt_save_cover">
<property name="text">
<string>Save &amp;cover separately</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="opt_replace_whitespace">
<property name="text">
<string>Replace space with &amp;underscores</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="opt_update_metadata">
<property name="text">
<string>Update &amp;metadata in saved copies</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="opt_to_lowercase">
<property name="text">
<string>Change paths to &amp;lowercase</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Format &amp;dates as:</string>
</property>
<property name="buddy">
<cstring>opt_timefmt</cstring>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="opt_timefmt"/>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>File &amp;formats to save:</string>
</property>
<property name="buddy">
<cstring>opt_formats</cstring>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLineEdit" name="opt_formats"/>
</item>
<item row="8" column="0" colspan="2">
<widget class="SaveTemplate" name="save_template" native="true"/>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="opt_asciiize">
<property name="text">
<string>Convert non-English characters to &amp;English equivalents</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="opt_write_opf">
<property name="text">
<string>Save metadata in &amp;OPF file</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SaveTemplate</class>
<extends>QWidget</extends>
<header>calibre/gui2/preferences/save_template.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -61,7 +61,7 @@ def config(defaults=None):
'actual e-book file(s).')) 'actual e-book file(s).'))
x('formats', default='all', x('formats', default='all',
help=_('Comma separated list of formats to save for each book.' 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, x('template', default=DEFAULT_TEMPLATE,
help=_('The template to control the filename and directory structure of the saved files. ' 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 ' 'Default is "%s" which will save books into a per-author '

View File

@ -490,6 +490,9 @@ class ConfigProxy(object):
setattr(self.__opts, key, val) setattr(self.__opts, key, val)
return self.__config.set(key, val) return self.__config.set(key, val)
def help(self, key):
return self.__config.get_option(key).help
class DynamicConfig(dict): class DynamicConfig(dict):
''' '''
A replacement for QSettings that supports dynamic config keys. A replacement for QSettings that supports dynamic config keys.