%PDF- %PDF-
Direktori : /lib/calibre/calibre/gui2/preferences/ |
Current File : //lib/calibre/calibre/gui2/preferences/metadata_sources.py |
#!/usr/bin/env python3 __license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>' __docformat__ = 'restructuredtext en' from operator import attrgetter from qt.core import ( QAbstractListModel, QAbstractTableModel, QDialogButtonBox, QFrame, QIcon, QLabel, QScrollArea, Qt, QVBoxLayout, QWidget, pyqtSignal, QDialog ) from calibre.customize.ui import ( all_metadata_plugins, default_disabled_plugins, disable_plugin, enable_plugin, is_disabled ) from calibre.ebooks.metadata.sources.prefs import msprefs from calibre.gui2 import error_dialog, question_dialog from calibre.gui2.preferences import ConfigWidgetBase, test_widget from calibre.gui2.preferences.metadata_sources_ui import Ui_Form from polyglot.builtins import iteritems class SourcesModel(QAbstractTableModel): # {{{ def __init__(self, parent=None): QAbstractTableModel.__init__(self, parent) self.gui_parent = parent self.plugins = [] self.enabled_overrides = {} self.cover_overrides = {} def initialize(self): self.beginResetModel() self.plugins = list(all_metadata_plugins()) self.plugins.sort(key=attrgetter('name')) self.enabled_overrides = {} self.cover_overrides = {} self.endResetModel() def rowCount(self, parent=None): return len(self.plugins) def columnCount(self, parent=None): return 2 def headerData(self, section, orientation, role): if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole: if section == 0: return _('Source') if section == 1: return _('Cover priority') return None def data(self, index, role): try: plugin = self.plugins[index.row()] except: return None col = index.column() if role in (Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.EditRole): if col == 0: return plugin.name elif col == 1: orig = msprefs['cover_priorities'].get(plugin.name, 1) return self.cover_overrides.get(plugin, orig) elif role == Qt.ItemDataRole.CheckStateRole and col == 0: orig = Qt.CheckState.Unchecked if is_disabled(plugin) else Qt.CheckState.Checked return self.enabled_overrides.get(plugin, orig) elif role == Qt.ItemDataRole.UserRole: return plugin elif (role == Qt.ItemDataRole.DecorationRole and col == 0 and not plugin.is_configured()): return QIcon(I('list_remove.png')) elif role == Qt.ItemDataRole.ToolTipRole: base = plugin.description + '\n\n' if plugin.is_configured(): return base + _('This source is configured and ready to go') return base + _('This source needs configuration') return None def setData(self, index, val, role): try: plugin = self.plugins[index.row()] except: return False col = index.column() ret = False if col == 0 and role == Qt.ItemDataRole.CheckStateRole: if val == Qt.CheckState.Checked and 'Douban' in plugin.name: if not question_dialog(self.gui_parent, _('Are you sure?'), '<p>'+ _('This plugin is useful only for <b>Chinese</b>' ' language books. It can return incorrect' ' results for books in English. Are you' ' sure you want to enable it?'), show_copy_button=False): return ret self.enabled_overrides[plugin] = int(val) ret = True if col == 1 and role == Qt.ItemDataRole.EditRole: try: self.cover_overrides[plugin] = max(1, int(val)) ret = True except (ValueError, TypeError): pass if ret: self.dataChanged.emit(index, index) return ret def flags(self, index): col = index.column() ans = QAbstractTableModel.flags(self, index) if col == 0: return ans | Qt.ItemFlag.ItemIsUserCheckable return Qt.ItemFlag.ItemIsEditable | ans def commit(self): for plugin, val in iteritems(self.enabled_overrides): if val == Qt.CheckState.Checked: enable_plugin(plugin) elif val == Qt.CheckState.Unchecked: disable_plugin(plugin) if self.cover_overrides: cp = msprefs['cover_priorities'] for plugin, val in iteritems(self.cover_overrides): if val == 1: cp.pop(plugin.name, None) else: cp[plugin.name] = val msprefs['cover_priorities'] = cp self.enabled_overrides = {} self.cover_overrides = {} def restore_defaults(self): self.beginResetModel() self.enabled_overrides = {p: (Qt.CheckState.Unchecked if p.name in default_disabled_plugins else Qt.CheckState.Checked) for p in self.plugins} self.cover_overrides = {p: msprefs.defaults['cover_priorities'].get(p.name, 1) for p in self.plugins} self.endResetModel() # }}} class FieldsModel(QAbstractListModel): # {{{ def __init__(self, parent=None): QAbstractTableModel.__init__(self, parent) self.fields = [] self.descs = { 'authors': _('Authors'), 'comments': _('Comments'), 'pubdate': _('Published date'), 'publisher': _('Publisher'), 'rating' : _('Rating'), 'tags' : _('Tags'), 'title': _('Title'), 'series': ngettext('Series', 'Series', 1), 'languages': _('Languages'), } self.overrides = {} self.exclude = frozenset([ 'series_index', 'language' # some plugins use language instead of languages ]) def rowCount(self, parent=None): return len(self.fields) def initialize(self): fields = set() for p in all_metadata_plugins(): fields |= p.touched_fields self.beginResetModel() self.fields = [] for x in fields: if not x.startswith('identifier:') and x not in self.exclude: self.fields.append(x) self.fields.sort(key=lambda x:self.descs.get(x, x)) self.endResetModel() def state(self, field, defaults=False): src = msprefs.defaults if defaults else msprefs return (Qt.CheckState.Unchecked if field in src['ignore_fields'] else Qt.CheckState.Checked) def data(self, index, role): try: field = self.fields[index.row()] except: return None if role == Qt.ItemDataRole.DisplayRole: return self.descs.get(field, field) if role == Qt.ItemDataRole.CheckStateRole: return self.overrides.get(field, self.state(field)) return None def flags(self, index): ans = QAbstractListModel.flags(self, index) return ans | Qt.ItemFlag.ItemIsUserCheckable def restore_defaults(self): self.beginResetModel() self.overrides = {f: self.state(f, Qt.CheckState.Checked) for f in self.fields} self.endResetModel() def select_all(self): self.beginResetModel() self.overrides = {f: Qt.CheckState.Checked for f in self.fields} self.endResetModel() def clear_all(self): self.beginResetModel() self.overrides = {f: Qt.CheckState.Unchecked for f in self.fields} self.endResetModel() def setData(self, index, val, role): try: field = self.fields[index.row()] except: return False ret = False if role == Qt.ItemDataRole.CheckStateRole: self.overrides[field] = int(val) ret = True if ret: self.dataChanged.emit(index, index) return ret def commit(self): ignored_fields = {x for x in msprefs['ignore_fields'] if x not in self.overrides} changed = {k for k, v in iteritems(self.overrides) if v == Qt.CheckState.Unchecked} msprefs['ignore_fields'] = list(ignored_fields.union(changed)) def user_default_state(self, field): return (Qt.CheckState.Unchecked if field in msprefs.get('user_default_ignore_fields',[]) else Qt.CheckState.Checked) def select_user_defaults(self): self.beginResetModel() self.overrides = {f: self.user_default_state(f) for f in self.fields} self.endResetModel() def commit_user_defaults(self): default_ignored_fields = {x for x in msprefs['user_default_ignore_fields'] if x not in self.overrides} changed = {k for k, v in iteritems(self.overrides) if v == Qt.CheckState.Unchecked} msprefs['user_default_ignore_fields'] = list(default_ignored_fields.union(changed)) # }}} class PluginConfig(QWidget): # {{{ finished = pyqtSignal() def __init__(self, plugin, parent): QWidget.__init__(self, parent) self.plugin = plugin self.l = l = QVBoxLayout() self.setLayout(l) self.c = c = QLabel(_('<b>Configure %(name)s</b><br>%(desc)s') % dict( name=plugin.name, desc=plugin.description)) c.setAlignment(Qt.AlignmentFlag.AlignHCenter) l.addWidget(c) self.config_widget = plugin.config_widget() self.sa = sa = QScrollArea(self) sa.setWidgetResizable(True) sa.setWidget(self.config_widget) l.addWidget(sa) self.bb = QDialogButtonBox( QDialogButtonBox.StandardButton.Save|QDialogButtonBox.StandardButton.Cancel, parent=self) self.bb.accepted.connect(self.finished) self.bb.rejected.connect(self.finished) self.bb.accepted.connect(self.commit) l.addWidget(self.bb) self.f = QFrame(self) self.f.setFrameShape(QFrame.Shape.HLine) l.addWidget(self.f) def commit(self): self.plugin.save_settings(self.config_widget) # }}} class ConfigWidget(ConfigWidgetBase, Ui_Form): def genesis(self, gui): r = self.register r('txt_comments', msprefs) r('max_tags', msprefs) r('wait_after_first_identify_result', msprefs) r('wait_after_first_cover_result', msprefs) r('swap_author_names', msprefs) r('fewer_tags', msprefs) r('keep_dups', msprefs) r('append_comments', msprefs) self.configure_plugin_button.clicked.connect(self.configure_plugin) self.sources_model = SourcesModel(self) self.sources_view.setModel(self.sources_model) self.sources_model.dataChanged.connect(self.changed_signal) self.fields_model = FieldsModel(self) self.fields_view.setModel(self.fields_model) self.fields_model.dataChanged.connect(self.changed_signal) self.select_all_button.clicked.connect(self.fields_model.select_all) self.select_all_button.clicked.connect(self.changed_signal) self.clear_all_button.clicked.connect(self.fields_model.clear_all) self.clear_all_button.clicked.connect(self.changed_signal) self.select_default_button.clicked.connect(self.fields_model.select_user_defaults) self.select_default_button.clicked.connect(self.changed_signal) self.set_as_default_button.clicked.connect(self.fields_model.commit_user_defaults) self.tag_map_rules = self.author_map_rules = None self.tag_map_rules_button.clicked.connect(self.change_tag_map_rules) self.author_map_rules_button.clicked.connect(self.change_author_map_rules) l = self.page.layout() l.setStretch(0, 1) l.setStretch(1, 1) def configure_plugin(self): for index in self.sources_view.selectionModel().selectedRows(): plugin = self.sources_model.data(index, Qt.ItemDataRole.UserRole) if plugin is not None: return self.do_config(plugin) error_dialog(self, _('No source selected'), _('No source selected, cannot configure.'), show=True) def do_config(self, plugin): self.pc = PluginConfig(plugin, self) self.stack.insertWidget(1, self.pc) self.stack.setCurrentIndex(1) self.pc.finished.connect(self.pc_finished) def pc_finished(self): try: self.pc.finished.diconnect() except: pass self.stack.setCurrentIndex(0) self.stack.removeWidget(self.pc) self.pc = None def change_tag_map_rules(self): from calibre.gui2.tag_mapper import RulesDialog d = RulesDialog(self) if msprefs.get('tag_map_rules'): d.rules = msprefs['tag_map_rules'] if d.exec() == QDialog.DialogCode.Accepted: self.tag_map_rules = d.rules self.changed_signal.emit() def change_author_map_rules(self): from calibre.gui2.author_mapper import RulesDialog d = RulesDialog(self) if msprefs.get('author_map_rules'): d.rules = msprefs['author_map_rules'] if d.exec() == QDialog.DialogCode.Accepted: self.author_map_rules = d.rules self.changed_signal.emit() def initialize(self): ConfigWidgetBase.initialize(self) self.sources_model.initialize() self.sources_view.resizeColumnsToContents() self.fields_model.initialize() self.tag_map_rules = self.author_map_rules = None def restore_defaults(self): ConfigWidgetBase.restore_defaults(self) self.sources_model.restore_defaults() self.fields_model.restore_defaults() self.changed_signal.emit() def commit(self): self.sources_model.commit() self.fields_model.commit() if self.tag_map_rules is not None: msprefs['tag_map_rules'] = self.tag_map_rules or [] if self.author_map_rules is not None: msprefs['author_map_rules'] = self.author_map_rules or [] return ConfigWidgetBase.commit(self) if __name__ == '__main__': from calibre.gui2 import Application app = Application([]) test_widget('Sharing', 'Metadata download')