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
|
||||
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,
|
||||
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