mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Preferences widget for saving to disk
This commit is contained in:
parent
d223636fd4
commit
9f8ce9dc89
@ -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]
|
||||
|
||||
#}}}
|
||||
|
||||
|
@ -5,10 +5,13 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__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()
|
||||
# }}}
|
||||
|
||||
|
||||
|
@ -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')
|
||||
|
||||
|
49
src/calibre/gui2/preferences/history.py
Normal file
49
src/calibre/gui2/preferences/history.py
Normal 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()
|
||||
|
||||
|
||||
|
68
src/calibre/gui2/preferences/save_template.py
Normal file
68
src/calibre/gui2/preferences/save_template.py
Normal 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')
|
||||
|
||||
|
||||
|
||||
|
||||
|
60
src/calibre/gui2/preferences/save_template.ui
Normal file
60
src/calibre/gui2/preferences/save_template.ui
Normal 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 &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>
|
52
src/calibre/gui2/preferences/saving.py
Normal file
52
src/calibre/gui2/preferences/saving.py
Normal 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')
|
||||
|
110
src/calibre/gui2/preferences/saving.ui
Normal file
110
src/calibre/gui2/preferences/saving.ui
Normal 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 &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 &underscores</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="opt_update_metadata">
|
||||
<property name="text">
|
||||
<string>Update &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 &lowercase</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Format &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 &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 &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 &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>
|
@ -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 '
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user