Package mathbench :: Package ext :: Module pluginmanagement_widgets
[hide private]

Source Code for Module mathbench.ext.pluginmanagement_widgets

  1  #----------------------------------------------------------------------------- 
  2  # Name:         pluginmanager_widgets.py 
  3  # Purpose:       global plugin management and dialog to control them 
  4  # 
  5  # Author:         Rob McMullen 
  6  # Comments:        Modified for mathbench by Thibauld Nion 
  7  # Created:       2007 
  8  # RCS-ID:         $Id: $ 
  9  # Copyright:   (c) 2007 Rob McMullen 
 10  # License:       wxWidgets 
 11  #----------------------------------------------------------------------------- 
 12  """ 
 13  Widget for plugin management configurations 
 14   
 15  This module provides plugin management for yapsy plugins. 
 16  """ 
 17   
 18  import os, sys 
 19  #from cStringIO import StringIO 
 20   
 21  import wx 
 22  # import wx.stc 
 23  # from wx.lib.pubsub import Publisher 
 24  # from wx.lib.evtmgr import eventManager 
 25  from wx.lib.mixins.listctrl import CheckListCtrlMixin 
 26  import wx.lib.hyperlink as hlk 
 27   
 28   
 29  import logging 
 30   
 31  from columnsizer import * 
 32   
 33   
34 -class PluginList(wx.ListCtrl, CheckListCtrlMixin, ColumnSizerMixin):
35 """ 36 Display the list of plugins with check boxes to see which ones are 37 activated. 38 39 Collaborates with a plugin manager. 40 """ 41
42 - def __init__(self, parent, pm, use_versions = True):
43 wx.ListCtrl.__init__(self, parent, size=(200,100), style=wx.LC_REPORT) 44 CheckListCtrlMixin.__init__(self) 45 ColumnSizerMixin.__init__(self) 46 47 self.plugin_manager = pm 48 self.recuperatePlugins() 49 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated) 50 51 self.skip_verify = False 52 self.show_versions = use_versions 53 54 self.createColumns() 55 self.reset() 56 self.resizeColumns()
57 58
59 - def recuperatePlugins(self):
60 """ 61 Recuperate all plugins 62 """ 63 cats = self.plugin_manager.getCategories() 64 self.plugins = [] 65 for cat in cats: 66 plugins = self.plugin_manager.getPluginsOfCategory(cat) 67 # print plugins 68 self.plugins.extend(plugins) 69 logging.debug("Recuperated plugins %s" % self.plugins) 70 71 # Sort first by name, then by version number 72 self.plugins.sort(key=lambda s:(s.name, s.version))
73 74
75 - def createColumns(self):
76 self.InsertColumn(0, "Name") 77 self.SetColumnWidth(0, wx.LIST_AUTOSIZE) 78 if self.show_versions: 79 self.InsertColumn(1, "Version") 80 self.SetColumnWidth(1, wx.LIST_AUTOSIZE)
81
82 - def getPlugin(self, index):
83 if len(self.plugins)==0: 84 return None 85 else: 86 return self.plugins[index]
87
88 - def OnItemActivated(self, evt):
89 index = evt.GetIndex() 90 logging.debug("selected plugin %d: %s" % (index, self.plugins[index])) 91 evt.Skip()
92
93 - def reset(self):
94 index = 0 95 list_count = self.GetItemCount() 96 for plugin in self.plugins: 97 logging.debug("Reset info for plugin: %s" % plugin.name) 98 if index >= list_count: 99 self.InsertStringItem(sys.maxint, plugin.name) 100 else: 101 self.SetStringItem(index, 0, plugin.name) 102 if self.show_versions: 103 self.SetStringItem(index, 1, str(plugin.version)) 104 self.CheckItem(index, plugin.is_activated) 105 106 index += 1 107 108 if index < list_count: 109 for i in range(index, list_count): 110 # always delete the first item because the list gets 111 # shorter by one each time. 112 self.DeleteItem(index)
113
114 - def OnCheckItem(self, index, flag):
115 """ 116 When an item "checked state" change, apply the change to the 117 corresponding plugin by activating/deactivating it. 118 """ 119 if flag: 120 what = "checked" 121 else: 122 what = "unchecked" 123 logging.debug("toggling plugin %d: %s = %s" % (index, self.plugins[index], 124 what)) 125 if not self.skip_verify: 126 # If we aren't currently verifying another item, verify 127 # that only one plugin of the same name is active 128 self.verify(index) 129 plugin = self.plugins[index]
130
131 - def verify(self, selected_index):
132 """ 133 Check two things: 134 135 - make sure a plugin that is currently in use isn't deactivated 136 137 - When multiple versions of the same plugin exist, verify 138 that only the selected version is active and all other 139 versions are deactivated. 140 """ 141 # CheckItem always calls OnCheckItem, so setting the checked 142 # state within this method would cause in infinite loop 143 # without this skip_verify flag 144 self.skip_verify = True 145 selected = self.plugins[selected_index] 146 147 # If the user tried to disable an in-use plugin, don't allow it 148 if hasattr(selected.plugin_object, 'isInUse'): 149 if not self.IsChecked(selected_index) and selected.plugin_object.isInUse(): 150 self.CheckItem(selected_index, True) 151 self.skip_verify = False 152 dlg = wx.MessageDialog(self, "Plugin %s in use.\n\nCan't deactivate a running plugin." % selected.name, "Plugin in use", wx.OK | wx.ICON_EXCLAMATION ) 153 dlg.ShowModal() 154 return 155 156 # otherwise, make sure the selected plugin doesn't have any 157 # other versions active 158 index = 0 159 for plugin in self.plugins: 160 if plugin.name == selected.name and plugin != selected: 161 self.CheckItem(index, False) 162 index += 1 163 self.skip_verify = False
164
165 - def update(self):
166 index = 0 167 for plugin in self.plugins: 168 activated = self.IsChecked(index) 169 if activated != plugin.plugin_object.is_activated: 170 if activated: 171 logging.debug("Activating %s" % plugin.plugin_object) 172 self.plugin_manager.activatePluginByName(plugin.name,plugin.category) 173 else: 174 logging.debug("Deactivating %s" % plugin.plugin_object) 175 self.plugin_manager.deactivatePluginByName(plugin.name,plugin.category) 176 index += 1
177 178
179 -class SimplePluginPanel(wx.Panel):
180 """ 181 A simple panel that shows basic info about the plugin and display 182 a 'Configure' button if the current plugin is configurable. 183 """ 184
185 - def __init__(self, parent, plugin_info):
186 """ 187 plugin_info: instance holding the plugin object and its metadata. 188 """ 189 self.plugin_info = plugin_info 190 wx.Panel.__init__(self, parent) 191 self.sizer = wx.GridBagSizer() 192 self.create() 193 self.SetSizer(self.sizer)
194
195 - def create(self):
196 row = 0 197 # set the main title 198 txt = wx.StaticText(self,-1,"Description") 199 font = txt.GetFont() 200 font.SetWeight(wx.FONTWEIGHT_BOLD) 201 txt.SetFont(font) 202 self.sizer.Add(txt,(row,1)) 203 row += 1 204 # kind of separator 205 sep0 = wx.StaticText(self, -1, "") 206 self.sizer.Add(sep0,(row,1),flag=wx.EXPAND) 207 row += 1 208 order = ['author', 'version', 'copyright'] 209 for info in order: 210 if (hasattr(self.plugin_info,info)): 211 title = wx.StaticText(self, -1, info+": ") 212 self.sizer.Add(title, (row,0)) 213 value = wx.StaticText(self, -1, str(getattr(self.plugin_info, info))) 214 self.sizer.Add(value, (row,1), flag=wx.EXPAND) 215 row += 1 216 # kind of separator 217 sep = wx.StaticText(self, -1, "") 218 self.sizer.Add(sep,(row,1),flag=wx.EXPAND) 219 row += 1 220 # Display the web link separately: 221 if (hasattr(self.plugin_info,'website')): 222 hyper1 = hlk.HyperLinkCtrl(self, wx.ID_ANY, "Website", 223 URL=self.plugin_info.website) 224 self.sizer.Add(hyper1, (row,1), flag=wx.EXPAND) 225 row += 1 226 # kind of separator 227 sep2 = wx.StaticText(self, -1, "") 228 self.sizer.Add(sep2,(row,1),flag=wx.EXPAND) 229 row += 1 230 # create a button that will show the plugin's files 231 if (hasattr(self.plugin_info,'path')): 232 def on_reveal(event): 233 """ 234 Reveal the plugin files in a file dialog 235 """ 236 dlg = wx.FileDialog( 237 self, message="Plugin's files", defaultDir=self.plugin_info.path, 238 defaultFile="", style=wx.FD_CHANGE_DIR 239 ) 240 dlg.ShowModal() 241 dlg.Destroy()
242 btn = wx.Button(self,-1,"Reveal files") 243 btn.Bind(wx.EVT_BUTTON,on_reveal) 244 self.sizer.Add(btn,(row,0),flag=wx.ALL,border=3) 245 # when the plugin provides a widget for its configuration, display it ! 246 if hasattr(self.plugin_info.plugin_object,"showConfigurationWidget"): 247 def on_click(event): 248 self.plugin_info.plugin_object.showConfigurationWidget(self)
249 btn = wx.Button(self,-1,"Configure") 250 btn.Bind(wx.EVT_BUTTON,on_click) 251 self.sizer.Add(btn,(row,1),flag=wx.ALL,border=3) 252
253 - def update(self):
254 # update preferences here 255 pass
256
257 -class NoPluginPanel(wx.Panel):
258 """ 259 A simple panel to be displayed when there is no plugin at all ! 260 """ 261
262 - def __init__(self, parent, pm):
263 """ 264 pm: plugin manager 265 """ 266 self.pm = pm 267 wx.Panel.__init__(self, parent) 268 self.sizer = wx.BoxSizer(wx.VERTICAL) 269 self.create() 270 self.SetSizer(self.sizer)
271 272
273 - def create(self):
274 txt = wx.StaticText(self, -1, """ 275 276 No plugin found or selected ! 277 278 """) 279 self.sizer.Add(txt)
280
281 - def update(self):
282 """ 283 dummy function to comply with the required interface; 284 """ 285 pass
286
287 -class PluginsConfigPanel(wx.Panel):
288 """ 289 Holds a list of plugins 290 """ 291
292 - def __init__(self, parent, pm, use_versions=True):
293 """ 294 pm: the plugin manager instance. 295 """ 296 297 wx.Panel.__init__(self, parent, -1) 298 299 self.plugin_manager = pm 300 301 self.show_versions = use_versions 302 303 sizer = wx.BoxSizer(wx.VERTICAL) 304 txt = wx.StaticText(self,-1,""" 305 Check/uncheck the plugins to ask to their activation/deactivation. 306 307 Press the 'Apply' button to enforce these choices. 308 """) 309 sizer.Add(txt,flag=wx.ALL|wx.EXPAND,border=3) 310 311 self.splitter = wx.SplitterWindow(self) 312 self.splitter.SetMinimumPaneSize(150) 313 listpanel = self.createListPanel(self.splitter) 314 self.list = listpanel.list 315 316 self.panels = {} 317 pref = self.createPanel(self.list.getPlugin(0)) 318 319 self.splitter.SplitVertically(listpanel, pref, 0) 320 # self.splitter.SetSashGravity(0.4) 321 sizer.Add(self.splitter, 1, wx.EXPAND) 322 323 self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected) 324 self.list.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated) 325 self.SetSizer(sizer) 326 self.Fit()
327
328 - def createListPanel(self, parent):
329 """ 330 Create a panel containing a list and an 'Apply' button. 331 """ 332 panel = wx.Panel(parent,-1) 333 sizer = wx.BoxSizer(wx.VERTICAL) 334 panel.list = self.createList(panel) 335 sizer.Add(panel.list,proportion=1,flag=wx.ALL|wx.EXPAND,border=3) 336 botpanel = wx.Panel(panel,-1) 337 botsizer = wx.BoxSizer(wx.HORIZONTAL) 338 btn = wx.Button(botpanel,-1,"Apply") 339 btn.Bind(wx.EVT_BUTTON,self.OnApply) 340 btn2 = wx.Button(botpanel,-1,"Add") 341 btn2.Bind(wx.EVT_BUTTON,self.OnAdd) 342 botsizer.Add(btn,flag=wx.ALL|wx.EXPAND,border=3) 343 botsizer.Add(btn2,flag=wx.ALL|wx.EXPAND,border=3) 344 botpanel.SetSizer(botsizer) 345 sizer.Add(botpanel,flag=wx.ALL|wx.EXPAND,border=3) 346 panel.SetSizer(sizer) 347 return panel
348
349 - def createList(self, parent):
350 """ 351 Create a list-display of all plugins. 352 """ 353 list = PluginList(parent, self.plugin_manager, use_versions=self.show_versions) 354 if len(list.plugins)!=0: 355 list.SetItemState(0, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) 356 return list
357
358 - def createPanel(self, plugin_info):
359 if plugin_info is None: 360 pref = NoPluginPanel(self.splitter,self.plugin_manager) 361 else: 362 pref = SimplePluginPanel(self.splitter, plugin_info) 363 self.panels[None] = pref 364 return pref
365
366 - def OnItemSelected(self, evt):
367 index = evt.GetIndex() 368 plugin_info = self.list.getPlugin(index) 369 if plugin_info: 370 if plugin_info in self.panels: 371 pref = self.panels[plugin_info] 372 else: 373 pref = self.createPanel(plugin_info) 374 old = self.splitter.GetWindow2() 375 self.splitter.ReplaceWindow(old, pref) 376 old.Hide() 377 pref.Show() 378 pref.Layout() 379 evt.Skip()
380
381 - def OnItemActivated(self, evt):
382 evt.Skip()
383
384 - def OnApply(self,event):
385 """ 386 Apply the activation/deactivation preferences 387 """ 388 self.applyPreferences()
389
390 - def OnAdd(self,event):
391 """ 392 Start the plugin addition procedure. 393 """ 394 self.install_a_plugin()
395
396 - def applyPreferences(self):
397 # Look at all the panels that have been created: this gives us 398 # an upper bound on what preferences may have changed. 399 for plugin, pref in self.panels.iteritems(): 400 pref.update() 401 402 # After the preferences have been changed, activate or 403 # deactivate plugins as required 404 self.list.update() 405 d = wx.MessageDialog(self, 406 """Changes have been applied. 407 408 Please restart for them to take effect.""", 409 "Plugin [de]activations", 410 style=wx.OK | wx.ICON_INFORMATION) 411 d.ShowModal() 412 d.Destroy()
413 414
415 - def install_a_plugin(self):
416 """ 417 Show dialog to help the user installing new plugins. 418 """ 419 d = wx.FileDialog(self, 420 message="Select the plugin to install", 421 wildcard="*.%s" % self.plugin_manager.plugin_info_ext, 422 style=wx.FD_OPEN) 423 ok_cancel = d.ShowModal() 424 current_directory = d.Directory 425 plugin_info_filename = d.Filename 426 d.Destroy() 427 if ok_cancel==wx.ID_OK and os.path.isfile(os.path.join(current_directory,plugin_info_filename)): 428 install_ok = self.plugin_manager.install(current_directory,plugin_info_filename) 429 if not install_ok: 430 d = wx.MessageDialog(self, 431 "Sorry, the required plugin could not be installed !", 432 "Plugin installation", 433 style=wx.OK | wx.ICON_ERROR) 434 d.ShowModal() 435 d.Destroy() 436 else: 437 self.plugin_manager.collectPlugins() 438 self.list.recuperatePlugins() 439 self.list.reset()
440 441 if __name__ == "__main__": 442 logging.basicConfig(level=logging.DEBUG) 443 444 sys.path.append(os.path.expanduser("~/src/mathbench/mathbenchdir/trunk")) 445 from mathbench.basement import plugin_manager as pman 446 447 PLUGINS_DIR= os.path.expanduser("~/src/mathbench/mathbenchdir/trunk/plugins") 448 import ConfigParser 449 # create the plugin manager 450 pm = pman.LabPluginManager.get() 451 pm.setConfigParser(ConfigParser.ConfigParser(), lambda : True) 452 pm.setCategoriesFilter({"Default":pman.IPlugin}) 453 pm.setPluginPlaces([PLUGINS_DIR]) 454 pm.setPluginInfoExtension("mathplug") 455 # load the plugins 456 pm.collectPlugins() 457 458 app = wx.PySimpleApp() 459 460 f = wx.Frame(None,title="Test plugin configuration",size=wx.Size(500,300)) 461 p = PluginsConfigPanel(parent=f, pm=pm, use_versions=False) 462 f.Show(True) 463 464 app.MainLoop() 465