mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Allow Export/Import of colomn coloring and icon rules. Fixes #1643314 [Enhancement: Save Icon and Column Coloring Rules](https://bugs.launchpad.net/calibre/+bug/1643314)
This commit is contained in:
parent
ed2a6fbf7b
commit
786fba9366
@ -7,19 +7,19 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os, textwrap
|
import os, textwrap, json
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt5.Qt import (QWidget, QDialog, QLabel, QGridLayout, QComboBox, QSize,
|
from PyQt5.Qt import (QWidget, QDialog, QLabel, QGridLayout, QComboBox, QSize,
|
||||||
QLineEdit, QIntValidator, QDoubleValidator, QFrame, Qt, QIcon,
|
QLineEdit, QIntValidator, QDoubleValidator, QFrame, Qt, QIcon, QHBoxLayout,
|
||||||
QScrollArea, QPushButton, QVBoxLayout, QDialogButtonBox, QToolButton,
|
QScrollArea, QPushButton, QVBoxLayout, QDialogButtonBox, QToolButton,
|
||||||
QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem,
|
QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem,
|
||||||
QApplication, QStandardItem, QStandardItemModel, QCheckBox, QMenu)
|
QApplication, QStandardItem, QStandardItemModel, QCheckBox, QMenu)
|
||||||
|
|
||||||
from calibre import prepare_string_for_xml, sanitize_file_name_unicode
|
from calibre import prepare_string_for_xml, sanitize_file_name_unicode, as_unicode
|
||||||
from calibre.constants import config_dir
|
from calibre.constants import config_dir
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.gui2 import error_dialog, choose_files, pixmap_to_data, gprefs
|
from calibre.gui2 import error_dialog, choose_files, pixmap_to_data, gprefs, choose_save_file
|
||||||
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
||||||
from calibre.gui2.metadata.single_download import RichTextDelegate
|
from calibre.gui2.metadata.single_download import RichTextDelegate
|
||||||
from calibre.gui2.widgets2 import ColorButton
|
from calibre.gui2.widgets2 import ColorButton
|
||||||
@ -679,6 +679,17 @@ class RuleEditor(QDialog): # {{{
|
|||||||
|
|
||||||
class RulesModel(QAbstractListModel): # {{{
|
class RulesModel(QAbstractListModel): # {{{
|
||||||
|
|
||||||
|
EXIM_VERSION = 1
|
||||||
|
|
||||||
|
def load_rule(self, col, template):
|
||||||
|
if col not in self.fm and col != color_row_key:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
rule = rule_from_template(self.fm, template)
|
||||||
|
except:
|
||||||
|
rule = template
|
||||||
|
return rule
|
||||||
|
|
||||||
def __init__(self, prefs, fm, pref_name, parent=None):
|
def __init__(self, prefs, fm, pref_name, parent=None):
|
||||||
QAbstractListModel.__init__(self, parent)
|
QAbstractListModel.__init__(self, parent)
|
||||||
|
|
||||||
@ -689,24 +700,16 @@ class RulesModel(QAbstractListModel): # {{{
|
|||||||
rules = list(prefs[pref_name])
|
rules = list(prefs[pref_name])
|
||||||
self.rules = []
|
self.rules = []
|
||||||
for col, template in rules:
|
for col, template in rules:
|
||||||
if col not in self.fm and col != color_row_key:
|
rule = self.load_rule(col, template)
|
||||||
continue
|
if rule is not None:
|
||||||
try:
|
|
||||||
rule = rule_from_template(self.fm, template)
|
|
||||||
except:
|
|
||||||
rule = template
|
|
||||||
self.rules.append(('color', col, rule))
|
self.rules.append(('color', col, rule))
|
||||||
else:
|
else:
|
||||||
self.rule_kind = 'icon' if pref_name == 'column_icon_rules' else 'emblem'
|
self.rule_kind = 'icon' if pref_name == 'column_icon_rules' else 'emblem'
|
||||||
rules = list(prefs[pref_name])
|
rules = list(prefs[pref_name])
|
||||||
self.rules = []
|
self.rules = []
|
||||||
for kind, col, template in rules:
|
for kind, col, template in rules:
|
||||||
if col not in self.fm and col != color_row_key:
|
rule = self.load_rule(col, template)
|
||||||
continue
|
if rule is not None:
|
||||||
try:
|
|
||||||
rule = rule_from_template(self.fm, template)
|
|
||||||
except:
|
|
||||||
rule = template
|
|
||||||
self.rules.append((kind, col, rule))
|
self.rules.append((kind, col, rule))
|
||||||
|
|
||||||
def rowCount(self, *args):
|
def rowCount(self, *args):
|
||||||
@ -742,17 +745,30 @@ class RulesModel(QAbstractListModel): # {{{
|
|||||||
self.rules.remove(self.rules[index.row()])
|
self.rules.remove(self.rules[index.row()])
|
||||||
self.endResetModel()
|
self.endResetModel()
|
||||||
|
|
||||||
def commit(self, prefs):
|
def rules_as_list(self, for_export=False):
|
||||||
rules = []
|
rules = []
|
||||||
for kind, col, r in self.rules:
|
for kind, col, r in self.rules:
|
||||||
if isinstance(r, Rule):
|
if isinstance(r, Rule):
|
||||||
r = r.template
|
r = r.template
|
||||||
if r is not None:
|
if r is not None:
|
||||||
if kind == 'color':
|
if not for_export and kind == 'color':
|
||||||
rules.append((col, r))
|
rules.append((col, r))
|
||||||
else:
|
else:
|
||||||
rules.append((kind, col, r))
|
rules.append((kind, col, r))
|
||||||
prefs[self.pref_name] = rules
|
return rules
|
||||||
|
|
||||||
|
def import_rules(self, rules):
|
||||||
|
self.beginResetModel()
|
||||||
|
for kind, col, template in rules:
|
||||||
|
if self.pref_name == 'column_color_rules':
|
||||||
|
kind = 'color'
|
||||||
|
rule = self.load_rule(col, template)
|
||||||
|
if rule is not None:
|
||||||
|
self.rules.append((kind, col, rule))
|
||||||
|
self.endResetModel()
|
||||||
|
|
||||||
|
def commit(self, prefs):
|
||||||
|
prefs[self.pref_name] = self.rules_as_list()
|
||||||
|
|
||||||
def move(self, idx, delta):
|
def move(self, idx, delta):
|
||||||
row = idx.row() + delta
|
row = idx.row() + delta
|
||||||
@ -906,7 +922,18 @@ class EditRules(QWidget): # {{{
|
|||||||
self.add_advanced_button = b = QPushButton(QIcon(I('plus.png')),
|
self.add_advanced_button = b = QPushButton(QIcon(I('plus.png')),
|
||||||
_('Add Advanced Rule'), self)
|
_('Add Advanced Rule'), self)
|
||||||
b.clicked.connect(self.add_advanced)
|
b.clicked.connect(self.add_advanced)
|
||||||
l.addWidget(b, l.rowCount(), 0, 1, 2)
|
self.hb = hb = QHBoxLayout()
|
||||||
|
l.addLayout(hb, l.rowCount(), 0, 1, 2)
|
||||||
|
hb.addWidget(b)
|
||||||
|
hb.addStretch(10)
|
||||||
|
self.export_button = b = QPushButton(_('E&xport'), self)
|
||||||
|
b.clicked.connect(self.export_rules)
|
||||||
|
b.setToolTip(_('Export these rules to a file'))
|
||||||
|
hb.addWidget(b)
|
||||||
|
self.import_button = b = QPushButton(_('&Import'), self)
|
||||||
|
b.setToolTip(_('Import rules from a file'))
|
||||||
|
b.clicked.connect(self.import_rules)
|
||||||
|
hb.addWidget(b)
|
||||||
|
|
||||||
def initialize(self, fm, prefs, mi, pref_name):
|
def initialize(self, fm, prefs, mi, pref_name):
|
||||||
self.pref_name = pref_name
|
self.pref_name = pref_name
|
||||||
@ -1051,8 +1078,38 @@ class EditRules(QWidget): # {{{
|
|||||||
if self.pref_name == 'cover_grid_icon_rules':
|
if self.pref_name == 'cover_grid_icon_rules':
|
||||||
gprefs['show_emblems'] = self.enabled.isChecked()
|
gprefs['show_emblems'] = self.enabled.isChecked()
|
||||||
|
|
||||||
|
def export_rules(self):
|
||||||
|
path = choose_save_file(self, 'export-coloring-rules', _('Choose file to export to'),
|
||||||
|
filters=[(_('Rules'), ['rules'])], all_files=False, initial_filename=self.pref_name + '.rules')
|
||||||
|
if path:
|
||||||
|
rules = {
|
||||||
|
'version': self.model.EXIM_VERSION,
|
||||||
|
'type': self.model.pref_name,
|
||||||
|
'rules': self.model.rules_as_list(for_export=True)
|
||||||
|
}
|
||||||
|
with lopen(path, 'wb') as f:
|
||||||
|
f.write(json.dumps(rules, indent=2))
|
||||||
|
|
||||||
|
def import_rules(self):
|
||||||
|
files = choose_files(self, 'import-coloring-rules', _('Choose file to import from'),
|
||||||
|
filters=[(_('Rules'), ['rules'])], all_files=False, select_only_single_file=True)
|
||||||
|
if files:
|
||||||
|
with lopen(files[0], 'rb') as f:
|
||||||
|
raw = f.read()
|
||||||
|
try:
|
||||||
|
rules = json.loads(raw)
|
||||||
|
if rules['version'] != self.model.EXIM_VERSION:
|
||||||
|
raise ValueError('Unsupported rules version: {}'.format(rules['version']))
|
||||||
|
if rules['type'] != self.pref_name:
|
||||||
|
raise ValueError('Rules are not of the correct type')
|
||||||
|
rules = list(rules['rules'])
|
||||||
|
except Exception as e:
|
||||||
|
return error_dialog(self, _('No valid rules found'), _(
|
||||||
|
'No valid rules were found in {}.').format(files[0]), det_msg=as_unicode(e), show=True)
|
||||||
|
self.model.import_rules(rules)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from calibre.gui2 import Application
|
from calibre.gui2 import Application
|
||||||
app = Application([])
|
app = Application([])
|
||||||
@ -1061,7 +1118,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
db = db()
|
db = db()
|
||||||
|
|
||||||
if True:
|
if False:
|
||||||
d = RuleEditor(db.field_metadata, 'column_icon_rules')
|
d = RuleEditor(db.field_metadata, 'column_icon_rules')
|
||||||
d.add_blank_condition()
|
d.add_blank_condition()
|
||||||
d.exec_()
|
d.exec_()
|
||||||
@ -1074,9 +1131,7 @@ if __name__ == '__main__':
|
|||||||
else:
|
else:
|
||||||
d = EditRules()
|
d = EditRules()
|
||||||
d.resize(QSize(800, 600))
|
d.resize(QSize(800, 600))
|
||||||
d.initialize(db.field_metadata, db.prefs, None)
|
d.initialize(db.field_metadata, db.prefs, None, 'column_color_rules')
|
||||||
d.show()
|
d.show()
|
||||||
app.exec_()
|
app.exec_()
|
||||||
d.commit(db.prefs)
|
d.commit(db.prefs)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user