mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Preferences widget for plugins
This commit is contained in:
parent
4a78f15205
commit
410359d2f6
@ -784,8 +784,18 @@ class Server(PreferencesPlugin):
|
|||||||
name_order = 2
|
name_order = 2
|
||||||
config_widget = 'calibre.gui2.preferences.server'
|
config_widget = 'calibre.gui2.preferences.server'
|
||||||
|
|
||||||
|
class Plugins(PreferencesPlugin):
|
||||||
|
name = 'Plugins'
|
||||||
|
gui_name = _('Plugins')
|
||||||
|
category = 'Advanced'
|
||||||
|
gui_category = _('Advanced')
|
||||||
|
category_order = 5
|
||||||
|
name_order = 1
|
||||||
|
config_widget = 'calibre.gui2.preferences.plugins'
|
||||||
|
|
||||||
plugins += [LookAndFeel, Behavior, Columns, Toolbar, InputOptions,
|
plugins += [LookAndFeel, Behavior, Columns, Toolbar, InputOptions,
|
||||||
CommonOptions, OutputOptions, Adding, Saving, Sending, Email, Server]
|
CommonOptions, OutputOptions, Adding, Saving, Sending, Email, Server,
|
||||||
|
Plugins]
|
||||||
|
|
||||||
#}}}
|
#}}}
|
||||||
|
|
||||||
|
241
src/calibre/gui2/preferences/plugins.py
Normal file
241
src/calibre/gui2/preferences/plugins.py
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
#!/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'
|
||||||
|
|
||||||
|
import textwrap, os
|
||||||
|
|
||||||
|
from PyQt4.Qt import Qt, QModelIndex, QAbstractItemModel, QVariant, QIcon, \
|
||||||
|
QBrush, QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QLineEdit
|
||||||
|
|
||||||
|
from calibre.gui2.preferences import ConfigWidgetBase, test_widget
|
||||||
|
from calibre.gui2.preferences.plugins_ui import Ui_Form
|
||||||
|
from calibre.customize.ui import initialized_plugins, is_disabled, enable_plugin, \
|
||||||
|
disable_plugin, customize_plugin, \
|
||||||
|
plugin_customization, add_plugin, \
|
||||||
|
remove_plugin
|
||||||
|
from calibre.gui2 import NONE, error_dialog, info_dialog, choose_files
|
||||||
|
|
||||||
|
class PluginModel(QAbstractItemModel): # {{{
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
QAbstractItemModel.__init__(self, *args)
|
||||||
|
self.icon = QVariant(QIcon(I('plugins.png')))
|
||||||
|
p = QIcon(self.icon).pixmap(32, 32, QIcon.Disabled, QIcon.On)
|
||||||
|
self.disabled_icon = QVariant(QIcon(p))
|
||||||
|
self._p = p
|
||||||
|
self.populate()
|
||||||
|
|
||||||
|
def populate(self):
|
||||||
|
self._data = {}
|
||||||
|
for plugin in initialized_plugins():
|
||||||
|
if plugin.type not in self._data:
|
||||||
|
self._data[plugin.type] = [plugin]
|
||||||
|
else:
|
||||||
|
self._data[plugin.type].append(plugin)
|
||||||
|
self.categories = sorted(self._data.keys())
|
||||||
|
|
||||||
|
for plugins in self._data.values():
|
||||||
|
plugins.sort(cmp=lambda x, y: cmp(x.name.lower(), y.name.lower()))
|
||||||
|
|
||||||
|
def index(self, row, column, parent):
|
||||||
|
if not self.hasIndex(row, column, parent):
|
||||||
|
return QModelIndex()
|
||||||
|
|
||||||
|
if parent.isValid():
|
||||||
|
return self.createIndex(row, column, 1+parent.row())
|
||||||
|
else:
|
||||||
|
return self.createIndex(row, column, 0)
|
||||||
|
|
||||||
|
def parent(self, index):
|
||||||
|
if not index.isValid() or index.internalId() == 0:
|
||||||
|
return QModelIndex()
|
||||||
|
return self.createIndex(index.internalId()-1, 0, 0)
|
||||||
|
|
||||||
|
def rowCount(self, parent):
|
||||||
|
if not parent.isValid():
|
||||||
|
return len(self.categories)
|
||||||
|
if parent.internalId() == 0:
|
||||||
|
category = self.categories[parent.row()]
|
||||||
|
return len(self._data[category])
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def columnCount(self, parent):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def index_to_plugin(self, index):
|
||||||
|
category = self.categories[index.parent().row()]
|
||||||
|
return self._data[category][index.row()]
|
||||||
|
|
||||||
|
def plugin_to_index(self, plugin):
|
||||||
|
for i, category in enumerate(self.categories):
|
||||||
|
parent = self.index(i, 0, QModelIndex())
|
||||||
|
for j, p in enumerate(self._data[category]):
|
||||||
|
if plugin == p:
|
||||||
|
return self.index(j, 0, parent)
|
||||||
|
return QModelIndex()
|
||||||
|
|
||||||
|
def refresh_plugin(self, plugin, rescan=False):
|
||||||
|
if rescan:
|
||||||
|
self.populate()
|
||||||
|
idx = self.plugin_to_index(plugin)
|
||||||
|
self.dataChanged.emit(idx, idx)
|
||||||
|
|
||||||
|
def flags(self, index):
|
||||||
|
if not index.isValid():
|
||||||
|
return 0
|
||||||
|
if index.internalId() == 0:
|
||||||
|
return Qt.ItemIsEnabled
|
||||||
|
flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
|
||||||
|
return flags
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if not index.isValid():
|
||||||
|
return NONE
|
||||||
|
if index.internalId() == 0:
|
||||||
|
if role == Qt.DisplayRole:
|
||||||
|
category = self.categories[index.row()]
|
||||||
|
return QVariant(_("%(plugin_type)s %(plugins)s")%\
|
||||||
|
dict(plugin_type=category, plugins=_('plugins')))
|
||||||
|
else:
|
||||||
|
plugin = self.index_to_plugin(index)
|
||||||
|
if role == Qt.DisplayRole:
|
||||||
|
ver = '.'.join(map(str, plugin.version))
|
||||||
|
desc = '\n'.join(textwrap.wrap(plugin.description, 50))
|
||||||
|
ans='%s (%s) %s %s\n%s'%(plugin.name, ver, _('by'), plugin.author, desc)
|
||||||
|
c = plugin_customization(plugin)
|
||||||
|
if c:
|
||||||
|
ans += _('\nCustomization: ')+c
|
||||||
|
return QVariant(ans)
|
||||||
|
if role == Qt.DecorationRole:
|
||||||
|
return self.disabled_icon if is_disabled(plugin) else self.icon
|
||||||
|
if role == Qt.ForegroundRole and is_disabled(plugin):
|
||||||
|
return QVariant(QBrush(Qt.gray))
|
||||||
|
if role == Qt.UserRole:
|
||||||
|
return plugin
|
||||||
|
return NONE
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||||
|
|
||||||
|
supports_restoring_to_defaults = False
|
||||||
|
|
||||||
|
def genesis(self, gui):
|
||||||
|
self.gui = gui
|
||||||
|
self._plugin_model = PluginModel()
|
||||||
|
self.plugin_view.setModel(self._plugin_model)
|
||||||
|
self.plugin_view.setStyleSheet(
|
||||||
|
"QTreeView::item { padding-bottom: 10px;}")
|
||||||
|
self.toggle_plugin_button.clicked.connect(self.toggle_plugin)
|
||||||
|
self.customize_plugin_button.clicked.connect(self.customize_plugin)
|
||||||
|
self.remove_plugin_button.clicked.connect(self.remove_plugin)
|
||||||
|
self.button_plugin_browse.clicked.connect(self.find_plugin)
|
||||||
|
self.button_plugin_add.clicked.connect(self.add_plugin)
|
||||||
|
|
||||||
|
def toggle_plugin(self, *args):
|
||||||
|
self.modify_plugin(op='toggle')
|
||||||
|
|
||||||
|
def customize_plugin(self, *args):
|
||||||
|
self.modify_plugin(op='customize')
|
||||||
|
|
||||||
|
def remove_plugin(self, *args):
|
||||||
|
self.modify_plugin(op='remove')
|
||||||
|
|
||||||
|
def add_plugin(self):
|
||||||
|
path = unicode(self.plugin_path.text())
|
||||||
|
if path and os.access(path, os.R_OK) and path.lower().endswith('.zip'):
|
||||||
|
add_plugin(path)
|
||||||
|
self._plugin_model.populate()
|
||||||
|
self._plugin_model.reset()
|
||||||
|
self.changed_signal.emit()
|
||||||
|
else:
|
||||||
|
error_dialog(self, _('No valid plugin path'),
|
||||||
|
_('%s is not a valid plugin path')%path).exec_()
|
||||||
|
|
||||||
|
def find_plugin(self):
|
||||||
|
path = choose_files(self, 'choose plugin dialog', _('Choose plugin'),
|
||||||
|
filters=[('Plugins', ['zip'])], all_files=False,
|
||||||
|
select_only_single_file=True)
|
||||||
|
if path:
|
||||||
|
self.plugin_path.setText(path[0])
|
||||||
|
|
||||||
|
def modify_plugin(self, op=''):
|
||||||
|
index = self.plugin_view.currentIndex()
|
||||||
|
if index.isValid():
|
||||||
|
plugin = self._plugin_model.index_to_plugin(index)
|
||||||
|
if op == 'toggle':
|
||||||
|
if not plugin.can_be_disabled:
|
||||||
|
error_dialog(self,_('Plugin cannot be disabled'),
|
||||||
|
_('The plugin: %s cannot be disabled')%plugin.name).exec_()
|
||||||
|
return
|
||||||
|
if is_disabled(plugin):
|
||||||
|
enable_plugin(plugin)
|
||||||
|
else:
|
||||||
|
disable_plugin(plugin)
|
||||||
|
self._plugin_model.refresh_plugin(plugin)
|
||||||
|
self.changed_signal.emit()
|
||||||
|
if op == 'customize':
|
||||||
|
if not plugin.is_customizable():
|
||||||
|
info_dialog(self, _('Plugin not customizable'),
|
||||||
|
_('Plugin: %s does not need customization')%plugin.name).exec_()
|
||||||
|
return
|
||||||
|
self.changed_signal.emit()
|
||||||
|
|
||||||
|
config_dialog = QDialog(self)
|
||||||
|
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||||
|
v = QVBoxLayout(config_dialog)
|
||||||
|
|
||||||
|
button_box.accepted.connect(config_dialog.accept)
|
||||||
|
button_box.rejected.connect(config_dialog.reject)
|
||||||
|
config_dialog.setWindowTitle(_('Customize') + ' ' + plugin.name)
|
||||||
|
|
||||||
|
if hasattr(plugin, 'config_widget'):
|
||||||
|
config_widget = plugin.config_widget()
|
||||||
|
v.addWidget(config_widget)
|
||||||
|
v.addWidget(button_box)
|
||||||
|
config_dialog.exec_()
|
||||||
|
|
||||||
|
if config_dialog.result() == QDialog.Accepted:
|
||||||
|
plugin.save_settings(config_widget)
|
||||||
|
self._plugin_model.refresh_plugin(plugin)
|
||||||
|
else:
|
||||||
|
help_text = plugin.customization_help(gui=True)
|
||||||
|
help_text = QLabel(help_text, config_dialog)
|
||||||
|
help_text.setWordWrap(True)
|
||||||
|
help_text.setTextInteractionFlags(Qt.LinksAccessibleByMouse
|
||||||
|
| Qt.LinksAccessibleByKeyboard)
|
||||||
|
help_text.setOpenExternalLinks(True)
|
||||||
|
v.addWidget(help_text)
|
||||||
|
sc = plugin_customization(plugin)
|
||||||
|
if not sc:
|
||||||
|
sc = ''
|
||||||
|
sc = sc.strip()
|
||||||
|
sc = QLineEdit(sc, config_dialog)
|
||||||
|
v.addWidget(sc)
|
||||||
|
v.addWidget(button_box)
|
||||||
|
config_dialog.exec_()
|
||||||
|
|
||||||
|
if config_dialog.result() == QDialog.Accepted:
|
||||||
|
sc = unicode(sc.text()).strip()
|
||||||
|
customize_plugin(plugin, sc)
|
||||||
|
|
||||||
|
self._plugin_model.refresh_plugin(plugin)
|
||||||
|
elif op == 'remove':
|
||||||
|
if remove_plugin(plugin):
|
||||||
|
self._plugin_model.populate()
|
||||||
|
self._plugin_model.reset()
|
||||||
|
self.changed_signal.emit()
|
||||||
|
else:
|
||||||
|
error_dialog(self, _('Cannot remove builtin plugin'),
|
||||||
|
plugin.name + _(' cannot be removed. It is a '
|
||||||
|
'builtin plugin. Try disabling it instead.')).exec_()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from PyQt4.Qt import QApplication
|
||||||
|
app = QApplication([])
|
||||||
|
test_widget('Advanced', 'Plugins')
|
||||||
|
|
141
src/calibre/gui2/preferences/plugins.ui
Normal file
141
src/calibre/gui2/preferences/plugins.ui
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<?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>723</width>
|
||||||
|
<height>540</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string>Here you can customize the behavior of Calibre by controlling what plugins it uses.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeView" name="plugin_view">
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>32</width>
|
||||||
|
<height>32</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="animated">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="headerHidden">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="toggle_plugin_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable/&Disable plugin</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="customize_plugin_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Customize plugin</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="remove_plugin_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Remove plugin</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_4">
|
||||||
|
<property name="title">
|
||||||
|
<string>Add new plugin</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_14">
|
||||||
|
<property name="text">
|
||||||
|
<string>Plugin &file:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>plugin_path</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="plugin_path"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="button_plugin_browse">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/document_open.png</normaloff>:/images/document_open.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="button_plugin_add">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Add</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../../../../resources/images.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
Loading…
x
Reference in New Issue
Block a user