03292: register plugins, load on need, not on start of GRAMPS - GEPS 014

Part 2: update plugin status: show registered, allow hide/unhide of 
        plugins


svn: r13403
This commit is contained in:
Benny Malengier 2009-10-24 20:27:10 +00:00
parent aab0acc340
commit 79b6708163
7 changed files with 274 additions and 40 deletions

View File

@ -45,8 +45,10 @@ import gobject
#-------------------------------------------------------------------------
import ManagedWindow
import Errors
from gen.plug import PluginManager
from gen.plug import PluginManager, PluginRegister, PTYPE_STR
import _Tool as Tool
from QuestionDialog import InfoDialog
import config
#-------------------------------------------------------------------------
#
@ -55,6 +57,9 @@ import _Tool as Tool
#-------------------------------------------------------------------------
class PluginStatus(ManagedWindow.ManagedWindow):
"""Displays a dialog showing the status of loaded plugins"""
HIDDEN = '<span color="red">%s</span>' % _('Hidden')
AVAILABLE = '<span weight="bold" color="blue">%s</span>'\
% _('Visible')
def __init__(self, uistate, track=[]):
self.__uistate = uistate
@ -63,6 +68,7 @@ class PluginStatus(ManagedWindow.ManagedWindow):
self.__class__)
self.__pmgr = PluginManager.get_instance()
self.__preg = PluginRegister.get_instance()
self.set_window(gtk.Dialog("", uistate.window,
gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)),
@ -70,6 +76,52 @@ class PluginStatus(ManagedWindow.ManagedWindow):
self.window.set_size_request(600, 400)
self.window.connect('response', self.close)
notebook = gtk.Notebook()
#first page with all registered plugins
vbox_reg = gtk.VBox()
scrolled_window_reg = gtk.ScrolledWindow()
self.list_reg = gtk.TreeView()
# model: plugintype, hidden, pluginname, plugindescr, pluginid
self.model_reg = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
self.selection_reg = self.list_reg.get_selection()
self.list_reg.set_model(self.model_reg)
self.list_reg.set_rules_hint(True)
self.list_reg.connect('button-press-event', self.button_press_reg)
col0_reg = gtk.TreeViewColumn(_('Type'), gtk.CellRendererText(), text=0)
col0_reg.set_sort_column_id(0)
self.list_reg.append_column(col0_reg)
self.list_reg.append_column(
gtk.TreeViewColumn(_('Hidden'), gtk.CellRendererText(), markup=1))
col2_reg = gtk.TreeViewColumn(_('Name'), gtk.CellRendererText(), text=2)
col2_reg.set_sort_column_id(2)
self.list_reg.append_column(col2_reg)
self.list_reg.append_column(
gtk.TreeViewColumn(_('Description'), gtk.CellRendererText(), text=3))
self.list_reg.set_search_column(2)
scrolled_window_reg.add(self.list_reg)
vbox_reg.pack_start(scrolled_window_reg)
hbutbox = gtk.HButtonBox()
hbutbox.set_layout(gtk.BUTTONBOX_SPREAD)
self.__info_btn = gtk.Button(_("Info"))
hbutbox.add(self.__info_btn)
self.__info_btn.connect('clicked', self.__info)
self.__hide_btn = gtk.Button(_("Hide/Unhide"))
hbutbox.add(self.__hide_btn)
self.__hide_btn.connect('clicked', self.__hide)
if __debug__:
self.__load_btn = gtk.Button(_("Load"))
hbutbox.add(self.__load_btn)
self.__load_btn.connect('clicked', self.__load)
vbox_reg.pack_start(hbutbox, expand=False, padding=5)
notebook.append_page(vbox_reg,
tab_label=gtk.Label(_('Registered plugins')))
#second page with loaded plugins
scrolled_window = gtk.ScrolledWindow()
self.list = gtk.TreeView()
self.model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
@ -91,7 +143,9 @@ class PluginStatus(ManagedWindow.ManagedWindow):
self.list.set_search_column(1)
scrolled_window.add(self.list)
self.window.vbox.add(scrolled_window)
notebook.append_page(scrolled_window,
tab_label=gtk.Label(_('Loaded plugins')))
self.window.vbox.add(notebook)
if __debug__:
# Only show the "Reload" button when in debug mode
@ -100,11 +154,19 @@ class PluginStatus(ManagedWindow.ManagedWindow):
self.window.action_area.add(self.__reload_btn)
self.__reload_btn.connect('clicked', self.__reload)
#obtain hidden plugins from the pluginmanager
self.hidden = self.__pmgr.get_hidden_plugin_ids()
self.window.show_all()
self.__populate_list()
self.__populate_lists()
def __populate_list(self):
""" Build the list of plugins """
def __populate_lists(self):
""" Build the lists of plugins """
self.__populate_load_list()
self.__populate_reg_list()
def __populate_load_list(self):
""" Build list of loaded plugins"""
fail_list = self.__pmgr.get_fail_list()
for i in fail_list:
@ -126,6 +188,28 @@ class PluginStatus(ManagedWindow.ManagedWindow):
self.model.append(row=[
'<span weight="bold" color="#267726">%s</span>' % _("OK"),
i[0], descr, None])
def __populate_reg_list(self):
""" Build list of registered plugins"""
for (type, typestr) in PTYPE_STR.iteritems():
for pdata in self.__preg.type_plugins(type):
# model: plugintype, hidden, pluginname, plugindescr, pluginid
hidden = pdata.id in self.hidden
if hidden:
hiddenstr = self.HIDDEN
else:
hiddenstr = self.AVAILABLE
self.model_reg.append(row=[
typestr, hiddenstr, pdata.name, pdata.description,
pdata.id])
def __rebuild_load_list(self):
self.model.clear()
self.__populate_load_list()
def __rebuild_reg_list(self):
self.model_reg.clear()
self.__populate_reg_list()
def button_press(self, obj, event):
""" Callback function from the user clicking on a line """
@ -142,9 +226,76 @@ class PluginStatus(ManagedWindow.ManagedWindow):
def __reload(self, obj):
""" Callback function from the "Reload" button """
self.__pmgr.reload_plugins()
self.model.clear()
self.__populate_list()
self.__rebuild_load_list()
self.__rebuild_reg_list()
def button_press_reg(self, obj, event):
""" Callback function from the user clicking on a line in reg plugin
"""
if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1:
self.__info(None)
def __info(self, obj):
""" Callback function from the "Info" button
"""
model, node = self.selection_reg.get_selected()
if not node:
return
id = model.get_value(node, 4)
typestr = model.get_value(node, 0)
pdata = self.__preg.get_plugin(id)
auth = ' - '.join(pdata.authors)
email = ' - '.join(pdata.authors_email)
if len(auth) > 60:
auth = auth[:60] + '...'
if len(email) > 60:
email = email[:60] + '...'
if pdata:
infotxt = """Plugin name: %(name)s [%(typestr)s]
Description: %(descr)s
Authors: %(authors)s
Email: %(email)s
Filename: %(fname)s
""" % {
'name': pdata.name,
'typestr': typestr,
'descr': pdata.description,
'authors': auth,
'email': email,
'fname': pdata.fname
}
InfoDialog('Detailed Info', infotxt, parent=self.window)
def __hide(self, obj):
""" Callback function from the "Hide" button
"""
model, node = self.selection_reg.get_selected()
if not node:
return
id = model.get_value(node, 4)
if id in self.hidden:
#unhide
self.hidden.remove(id)
model.set_value(node, 1, self.AVAILABLE)
self.__pmgr.unhide_plugin(id)
else:
#hide
self.hidden.add(id)
model.set_value(node, 1, self.HIDDEN)
self.__pmgr.hide_plugin(id)
def __load(self, obj):
""" Callback function from the "Load" button
"""
model, node = self.selection_reg.get_selected()
if not node:
return
id = model.get_value(node, 4)
pdata = self.__preg.get_plugin(id)
self.__pmgr.load_plugin(pdata)
self.__rebuild_load_list()
#-------------------------------------------------------------------------
#
# Details for an individual plugin that failed

View File

@ -643,6 +643,8 @@ register('researcher.researcher-phone', '')
register('researcher.researcher-postal', '')
register('researcher.researcher-state', '')
register('plugin.hiddenplugins', [])
#---------------------------------------------------------------
#
# Upgrade Conversions go here.

View File

@ -29,7 +29,7 @@ from _pluginreg import (PluginData, PluginRegister, REPORT, TOOL,
TOOL_UTILS, CATEGORY_QR_MISC, CATEGORY_QR_PERSON,
CATEGORY_QR_FAMILY, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE,
CATEGORY_QR_PLACE, CATEGORY_QR_REPOSITORY, CATEGORY_QR_NOTE,
CATEGORY_QR_DATE )
CATEGORY_QR_DATE, PTYPE_STR )
from _manager import PluginManager
from _import import ImportPlugin
from _export import ExportPlugin
@ -45,4 +45,4 @@ __all__ = [ "docbackend", "docgen", "menu", Plugin, PluginData,
TOOL_UTILS, CATEGORY_QR_MISC, CATEGORY_QR_PERSON,
CATEGORY_QR_FAMILY, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE,
CATEGORY_QR_PLACE, CATEGORY_QR_REPOSITORY, CATEGORY_QR_NOTE,
CATEGORY_QR_DATE]
CATEGORY_QR_DATE, PTYPE_STR]

View File

@ -47,6 +47,7 @@ from gettext import gettext as _
import gen.utils
import Relationship
from gen.plug import PluginRegister
import config
#-------------------------------------------------------------------------
#
@ -97,6 +98,21 @@ class PluginManager(gen.utils.Callback):
self.__pgr = PluginRegister.get_instance()
self.__registereddir_set = set()
self.__loaded_plugins = {}
self.__hidden_plugins = set([])
for id in config.get('plugin.hiddenplugins'):
self.__hidden_plugins.add(id)
self.__hidden_changed()
def __hidden_changed(self, *args):
#if hidden changed, stored data must be emptied as it could contain
#something that now must be hidden
self.__import_plugins = []
self.__export_plugins = []
self.__docgen_plugins = []
#objects that need to know if the plugins available changed, are
#listening to this signal to update themselves. If a plugin becomes
#(un)hidden, this should happen, so we emit.
self.emit('plugins-reloaded')
def reg_plugins(self, direct):
"""
@ -197,6 +213,33 @@ class PluginManager(gen.utils.Callback):
self.emit('plugins-reloaded')
def get_hidden_plugin_ids(self):
"""
Returns copy of the set hidden plugin ids
"""
return self.__hidden_plugins.copy()
def hide_plugin(self, id):
""" Hide plugin with given id. This will hide the plugin so queries do
not return it anymore, and write this change to the config.
Note that config will then emit a signal
"""
self.__hidden_plugins.add(id)
hideset = [x for x in self.__hidden_plugins]
config.set('plugin.hiddenplugins', hideset)
config.save()
self.__hidden_changed()
def unhide_plugin(self, id):
""" Unhide plugin with given id. This will unhide the plugin so queries
return it again, and write this change to the config
"""
self.__hidden_plugins.remove(id)
hideset = [x for x in self.__hidden_plugins]
config.set('plugin.hiddenplugins', hideset)
config.save()
self.__hidden_changed()
def get_fail_list(self):
""" Return the list of failed plugins. """
return self.__failmsg_list
@ -206,33 +249,38 @@ class PluginManager(gen.utils.Callback):
return self.__success_list
def get_reg_reports(self, gui=True):
""" Return list of registered reports
""" Return list of non hidden registered reports
:Param gui: bool indicating if GUI reports or CLI reports must be
returned
"""
return self.__pgr.report_plugins(gui)
return [plg for plg in self.__pgr.report_plugins(gui) if plg.id not in
self.__hidden_plugins]
def get_reg_tools(self, gui=True):
""" Return list of registered tools
""" Return list of non hidden registered tools
:Param gui: bool indicating if GUI reports or CLI reports must be
returned
"""
return self.__pgr.tool_plugins(gui)
return [plg for plg in self.__pgr.tool_plugins(gui) if plg.id not in
self.__hidden_plugins]
def get_reg_quick_reports(self):
""" Return list of registered quick reports
""" Return list of non hidden registered quick reports
"""
return self.__pgr.quickreport_plugins()
return [plg for plg in self.__pgr.quickreport_plugins() if plg.id not in
self.__hidden_plugins]
def get_reg_mapservices(self):
""" Return list of registered mapservices
""" Return list of non hidden registered mapservices
"""
return self.__pgr.mapservice_plugins()
return [plg for plg in self.__pgr.mapservice_plugins() if plg.id not in
self.__hidden_plugins]
def get_reg_bookitems(self):
""" Return list of reports registered as bookitem
""" Return list of non hidden reports registered as bookitem
"""
return self.__pgr.bookitem_plugins()
return [plg for plg in self.__pgr.bookitem_plugins() if plg.id not in
self.__hidden_plugins]
def get_external_opt_dict(self):
""" Return the dictionary of external options. """
@ -252,7 +300,8 @@ class PluginManager(gen.utils.Callback):
## only PluginData, loading from module when importfunction needed?
if self.__import_plugins == []:
#The module still needs to be imported
imps = self.__pgr.import_plugins()
imps = [pdata for pdata in self.__pgr.import_plugins() if pdata.id
not in self.__hidden_plugins]
for pdata in imps:
mod = self.load_plugin(pdata)
if mod:
@ -274,7 +323,8 @@ class PluginManager(gen.utils.Callback):
## only PluginData, loading from module when export/options needed?
if self.__export_plugins == []:
#The modules still need to be imported
exps = self.__pgr.export_plugins()
exps = [pdata for pdata in self.__pgr.export_plugins() if pdata.id
not in self.__hidden_plugins]
for pdata in exps:
mod = self.load_plugin(pdata)
if mod:
@ -299,7 +349,8 @@ class PluginManager(gen.utils.Callback):
## So, only do import when docgen.get_basedoc() is requested
if self.__docgen_plugins == []:
#The modules still need to be imported
dgdps = self.__pgr.docgen_plugins()
dgdps = [pdata for pdata in self.__pgr.docgen_plugins() if pdata.id
not in self.__hidden_plugins]
for pdata in dgdps:
mod = self.load_plugin(pdata)
if mod:

View File

@ -67,6 +67,19 @@ RELCALC = 9
GRAMPLET = 10
PTYPE = [ REPORT , QUICKREPORT, TOOL, IMPORT,
EXPORT, DOCGEN, GENERAL, MAPSERVICE, VIEW, RELCALC, GRAMPLET]
PTYPE_STR = {
REPORT: _('Report') ,
QUICKREPORT: _('Quickreport'),
TOOL: _('Tool'),
IMPORT: _('Importer'),
EXPORT: _('Exporter'),
DOCGEN: _('Doc creator'),
GENERAL: _('Plugin lib'),
MAPSERVICE: _('Map service'),
VIEW: _('GRAMPS View'),
RELCALC: _('Relationships'),
GRAMPLET: _('Gramplet'),
}
#possible report categories
CATEGORY_TEXT = 0
@ -834,7 +847,14 @@ class PluginRegister(object):
for ind in rmlist:
del self.__plugindata[ind]
def __type_plugins(self, ptype):
def get_plugin(self, id):
"""Return the PluginData for the plugin with id"""
for x in self.__plugindata:
if x.id == id:
return x
return None
def type_plugins(self, ptype):
"""Return a list of PluginData that are of type ptype
"""
return [x for x in self.__plugindata if x.ptype == ptype]
@ -844,68 +864,68 @@ class PluginRegister(object):
:param gui: bool, if True then gui plugin, otherwise cli plugin
"""
if gui:
return [x for x in self.__type_plugins(REPORT) if REPORT_MODE_GUI
return [x for x in self.type_plugins(REPORT) if REPORT_MODE_GUI
in x.report_modes]
else:
return [x for x in self.__type_plugins(REPORT) if REPORT_MODE_CLI
return [x for x in self.type_plugins(REPORT) if REPORT_MODE_CLI
in x.report_modes]
def tool_plugins(self, gui=True):
"""Return a list of PluginData that are of type TOOL
"""
if gui:
return [x for x in self.__type_plugins(TOOL) if TOOL_MODE_GUI
return [x for x in self.type_plugins(TOOL) if TOOL_MODE_GUI
in x.tool_modes]
else:
return [x for x in self.__type_plugins(TOOL) if TOOL_MODE_CLI
return [x for x in self.type_plugins(TOOL) if TOOL_MODE_CLI
in x.tool_modes]
def bookitem_plugins(self):
"""Return a list of REPORT PluginData that are can be used as bookitem
"""
return [x for x in self.__type_plugins(REPORT) if REPORT_MODE_BKI
return [x for x in self.type_plugins(REPORT) if REPORT_MODE_BKI
in x.report_modes]
def quickreport_plugins(self):
"""Return a list of PluginData that are of type QUICKREPORT
"""
return self.__type_plugins(QUICKREPORT)
return self.type_plugins(QUICKREPORT)
def import_plugins(self):
"""Return a list of PluginData that are of type IMPORT
"""
return self.__type_plugins(IMPORT)
return self.type_plugins(IMPORT)
def export_plugins(self):
"""Return a list of PluginData that are of type EXPORT
"""
return self.__type_plugins(EXPORT)
return self.type_plugins(EXPORT)
def docgen_plugins(self):
"""Return a list of PluginData that are of type DOCGEN
"""
return self.__type_plugins(DOCGEN)
return self.type_plugins(DOCGEN)
def general_plugins(self):
"""Return a list of PluginData that are of type GENERAL
"""
return self.__type_plugins(GENERAL)
return self.type_plugins(GENERAL)
def mapservice_plugins(self):
"""Return a list of PluginData that are of type MAPSERVICE
"""
return self.__type_plugins(MAPSERVICE)
return self.type_plugins(MAPSERVICE)
def view_plugins(self):
"""Return a list of PluginData that are of type VIEW
"""
return self.__type_plugins(RELCALC)
return self.type_plugins(VIEW)
def relcalc_plugins(self):
"""Return a list of PluginData that are of type RELCALC
"""
return self.__type_plugins(RELCALC)
return self.type_plugins(RELCALC)
def filter_load_on_reg(self):
"""Return a list of PluginData that have load_on_reg == True

View File

@ -223,6 +223,10 @@ class ViewManager(CLIManager):
self.buttons = []
self.merge_ids = []
self._key = None
self.toolactions = None
self.tool_menu_ui_id = None
self.reportactions = None
self.report_menu_ui_id = None
self.show_sidebar = config.get('interface.view')
self.show_toolbar = config.get('interface.toolbar-on')
@ -1290,12 +1294,15 @@ class ViewManager(CLIManager):
"""
Builds a new tools menu
"""
if self.toolactions:
self.uistate.uimanager.remove_action_group(self.toolactions)
self.uistate.uimanager.remove_ui(self.tool_menu_ui_id)
self.toolactions = gtk.ActionGroup('ToolWindow')
(uidef, actions) = self.build_plugin_menu(
'ToolsMenu', tool_menu_list, Tool.tool_categories,
make_plugin_callback)
self.toolactions.add_actions(actions)
self.uistate.uimanager.add_ui_from_string(uidef)
self.tool_menu_ui_id = self.uistate.uimanager.add_ui_from_string(uidef)
self.uimanager.insert_action_group(self.toolactions, 1)
self.uistate.uimanager.ensure_update()
@ -1303,12 +1310,15 @@ class ViewManager(CLIManager):
"""
Builds a new reports menu
"""
if self.reportactions:
self.uistate.uimanager.remove_action_group(self.reportactions)
self.uistate.uimanager.remove_ui(self.report_menu_ui_id)
self.reportactions = gtk.ActionGroup('ReportWindow')
(uidef, actions) = self.build_plugin_menu(
'ReportsMenu', report_menu_list, ReportBase.standalone_categories,
make_plugin_callback)
self.reportactions.add_actions(actions)
self.uistate.uimanager.add_ui_from_string(uidef)
self.report_menu_ui_id = self.uistate.uimanager.add_ui_from_string(uidef)
self.uimanager.insert_action_group(self.reportactions, 1)
self.uistate.uimanager.ensure_update()

View File

@ -28,7 +28,7 @@ Web Calendar generator.
Refactoring. This is an ongoing job until this plugin is in a better shape.
"""
from __future__ import with_statement
#------------------------------------------------------------------------
#
# python modules