%PDF- %PDF-
| Direktori : /usr/lib/calibre/calibre/gui2/preferences/ |
| Current File : //usr/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')