1
2
3
4
5
6
7
8
9
10
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
20
21 import wx
22
23
24
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):
57
58
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
68 self.plugins.extend(plugins)
69 logging.debug("Recuperated plugins %s" % self.plugins)
70
71
72 self.plugins.sort(key=lambda s:(s.name, s.version))
73
74
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
83 if len(self.plugins)==0:
84 return None
85 else:
86 return self.plugins[index]
87
89 index = evt.GetIndex()
90 logging.debug("selected plugin %d: %s" % (index, self.plugins[index]))
91 evt.Skip()
92
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
111
112 self.DeleteItem(index)
113
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
127
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
142
143
144 self.skip_verify = True
145 selected = self.plugins[selected_index]
146
147
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
157
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
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
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
196 row = 0
197
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
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
217 sep = wx.StaticText(self, -1, "")
218 self.sizer.Add(sep,(row,1),flag=wx.EXPAND)
219 row += 1
220
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
227 sep2 = wx.StaticText(self, -1, "")
228 self.sizer.Add(sep2,(row,1),flag=wx.EXPAND)
229 row += 1
230
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
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
256
258 """
259 A simple panel to be displayed when there is no plugin at all !
260 """
261
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
274 txt = wx.StaticText(self, -1, """
275
276 No plugin found or selected !
277
278 """)
279 self.sizer.Add(txt)
280
282 """
283 dummy function to comply with the required interface;
284 """
285 pass
286
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
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
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
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
365
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
383
385 """
386 Apply the activation/deactivation preferences
387 """
388 self.applyPreferences()
389
391 """
392 Start the plugin addition procedure.
393 """
394 self.install_a_plugin()
395
397
398
399 for plugin, pref in self.panels.iteritems():
400 pref.update()
401
402
403
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
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
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
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