1 """gmPlugin - base classes for GNUmed Horst space notebook plugins.
2
3 @copyright: author
4 """
5
6 __version__ = "$Revision: 1.85 $"
7 __author__ = "H.Herb, I.Haywood, K.Hilbert"
8 __license__ = 'GPL v2 or later (details at http://www.gnu.org)'
9
10 import os, sys, re, glob, logging
11
12
13 import wx
14
15
16 if __name__ == '__main__':
17 sys.path.insert(0, '../../')
18 from Gnumed.pycommon import gmExceptions, gmGuiBroker, gmCfg, gmDispatcher, gmTools
19 from Gnumed.business import gmPerson, gmSurgery
20
21 _log = logging.getLogger('gm.ui')
22 _log.info(__version__)
23
24
27 wx.ProgressDialog.__init__(
28 self,
29 title = _("GNUmed: configuring [%s] (%s plugins)") % (gmSurgery.gmCurrentPractice().active_workplace, nr_plugins),
30 message = _("loading list of plugins "),
31 maximum = nr_plugins,
32 parent = None,
33 style = wx.PD_ELAPSED_TIME
34 )
35 self.SetIcon(gmTools.get_icon(wx = wx))
36 self.idx = 0
37 self.nr_plugins = nr_plugins
38 self.prev_plugin = ""
39
40 - def Update (self, result, plugin):
41 if result == -1:
42 result = ""
43 elif result == 0:
44 result = _("failed")
45 else:
46 result = _("success")
47 wx.ProgressDialog.Update (self,
48 self.idx,
49 _("previous: %s (%s)\ncurrent (%s/%s): %s") % (
50 self.prev_plugin,
51 result,
52 (self.idx+1),
53 self.nr_plugins,
54 plugin))
55 self.prev_plugin = plugin
56 self.idx += 1
57
58
59
60
62 """Base class for plugins which provide a full notebook page.
63 """
65 self.gb = gmGuiBroker.GuiBroker()
66 self._set = 'gui'
67 self._widget = None
68 self.__register_events()
69
70
71
73 """Register ourselves with the main notebook widget."""
74
75 _log.info("set: [%s] class: [%s] name: [%s]" % (self._set, self.__class__.__name__, self.name()))
76
77
78 nb = self.gb['horstspace.notebook']
79 widget = self.GetWidget(nb)
80
81
82
83
84
85
86
87
88
89
90
91
92
93 nb.AddPage(widget, self.name())
94
95
96 self.gb['horstspace.notebook.%s' % self._set][self.__class__.__name__] = self
97 self.gb['horstspace.notebook.pages'].append(self)
98
99
100 menu_info = self.MenuInfo()
101 if menu_info is None:
102
103 gmDispatcher.send(signal = u'plugin_loaded', plugin_name = self.name(), class_name = self.__class__.__name__)
104 else:
105 name_of_menu, menu_item_name = menu_info
106 gmDispatcher.send (
107 signal = u'plugin_loaded',
108 plugin_name = menu_item_name,
109 class_name = self.__class__.__name__,
110 menu_name = name_of_menu,
111 menu_item_name = menu_item_name,
112
113 menu_help_string = self.name()
114 )
115
116 return True
117
119 """Remove ourselves."""
120 del self.gb['horstspace.notebook.%s' % self._set][self.__class__.__name__]
121 _log.info("plugin: [%s] (class: [%s]) set: [%s]" % (self.name(), self.__class__.__name__, self._set))
122
123
124 menu_info = self.MenuInfo()
125 if menu_info is not None:
126 menu = self.gb['main.%smenu' % menu_info[0]]
127 menu.Delete(self.menu_id)
128
129
130
131
132
133
134 nb_pages = self.gb['horstspace.notebook.pages']
135 nb_page_num = nb_pages.index(self)
136 del nb_pages[nb_page_num]
137
138
139 nb = self.gb['horstspace.notebook']
140 nb.DeletePage(nb_page_num)
141
143 return 'plugin <%s>' % self.__class__.__name__
144
146 """Return tuple of (menuname, menuitem).
147
148 None: no menu entry wanted
149 """
150 return None
151
152
153
154
155
156
157
158
159
160
161
163 """Called when this plugin is *about to* receive focus.
164
165 If None returned from here (or from overriders) the
166 plugin activation will be veto()ed (if it can be).
167 """
168
169 return True
170
172 """We *are* receiving focus via wx.EVT_NotebookPageChanged.
173
174 This can be used to populate the plugin widget on receiving focus.
175 """
176 if hasattr(self._widget, 'repopulate_ui'):
177 self._widget.repopulate_ui()
178
179 return True
180
182 """Check for patient availability.
183
184 - convenience method for your can_receive_focus() handlers
185 """
186
187 pat = gmPerson.gmCurrentPatient()
188 if not pat.connected:
189
190 gmDispatcher.send('statustext', msg = _('Cannot switch to [%s]: no patient selected') % self.name())
191 return None
192 return 1
193
195 """Raise ourselves."""
196 nb_pages = self.gb['horstspace.notebook.pages']
197 plugin_page = nb_pages.index(self)
198 nb = self.gb['horstspace.notebook']
199 nb.SetSelection(plugin_page)
200 return True
201
207
209
210 if kwds['name'] not in [self.__class__.__name__, self.name()]:
211 return False
212 return self._on_raise_by_menu(None)
213
214
218
219
222
225
227 """This mixin adds listening to patient change signals."""
231
233 print "%s._pre_patient_selection() not implemented" % self.__class__.__name__
234 print "should usually be used to commit unsaved data"
235
236 - def _post_patient_selection(self, **kwds):
237 print "%s._post_patient_selection() not implemented" % self.__class__.__name__
238 print "should usually be used to initialize state"
239
240
241
243 """Import a module.
244
245 I am not sure *why* we need this. But the docs
246 and Google say so. It's got something to do with
247 package imports returning the toplevel package name."""
248 try:
249 mod = __import__(module_name)
250 except ImportError:
251 _log.exception ('Cannot __import__() module [%s].' % module_name)
252 return None
253 components = module_name.split('.')
254 for component in components[1:]:
255 mod = getattr(mod, component)
256 return mod
257
259 """Instantiates a plugin object from a package directory, returning the object.
260
261 NOTE: it does NOT call register() for you !!!!
262
263 - "set" specifies the subdirectory in which to find the plugin
264 - this knows nothing of databases, all it does is instantiate a named plugin
265
266 There will be a general 'gui' directory for large GUI
267 components: prescritions, etc., then several others for more
268 specific types: export/import filters, crypto algorithms
269 guibroker, dbbroker are broker objects provided
270 defaults are the default set of plugins to be loaded
271
272 FIXME: we should inform the user about failing plugins
273 """
274
275 gb = gmGuiBroker.GuiBroker()
276
277
278 if not ('horstspace.notebook.%s' % aPackage) in gb.keylist():
279 gb['horstspace.notebook.%s' % aPackage] = {}
280 if not 'horstspace.notebook.pages' in gb.keylist():
281 gb['horstspace.notebook.pages'] = []
282
283 module_from_package = __gm_import('Gnumed.wxpython.%s.%s' % (aPackage, plugin_name))
284
285 plugin_class = module_from_package.__dict__[plugin_name]
286
287 if not issubclass(plugin_class, cNotebookPlugin):
288 _log.error("[%s] not a subclass of cNotebookPlugin" % plugin_name)
289 return None
290
291 _log.info(plugin_name)
292 try:
293 plugin = plugin_class()
294 except:
295 _log.exception('Cannot open module "%s.%s".' % (aPackage, plugin_name))
296 return None
297
298 return plugin
299
301 """Looks for installed plugins in the filesystem.
302
303 The first directory in sys.path which contains a wxpython/gui/
304 is considered the one -- because that's where the import will
305 get it from.
306 """
307 search_path = None
308 for path in sys.path:
309 tmp = os.path.join(path, 'Gnumed', 'wxpython', plugin_dir)
310 if os.path.exists(tmp):
311 search_path = tmp
312 break
313 if search_path is None:
314 _log.error('unable to find any candidate directory matching [$candidate/Gnumed/wxpython/%s/]' % plugin_dir)
315 _log.error('candidates: %s' % str(sys.path))
316 return []
317
318 _log.info("scanning plugin directory [%s]" % search_path)
319
320 files = glob.glob(os.path.join(search_path, 'gm*.py'))
321 plugins = []
322 for file in files:
323 path, fname = os.path.split(file)
324 mod_name, ext = os.path.splitext(fname)
325 plugins.append(mod_name)
326
327 _log.debug("plugins found: %s" % str(plugins))
328
329 return plugins
330
332 """Get a list of plugins to load.
333
334 1) from database if option is not None
335 2) from list of defaults
336 3) if 2 is None, from source directory (then stored in database)
337
338 FIXME: NOT from files in directories (important for py2exe)
339 """
340 if workplace == u'System Fallback':
341 return [u'gmProviderInboxPlugin', u'gmDataMiningPlugin']
342
343 if workplace is None:
344 workplace = gmSurgery.gmCurrentPractice().active_workplace
345
346 p_list = None
347
348 if option is not None:
349 dbcfg = gmCfg.cCfgSQL()
350 p_list = dbcfg.get2 (
351 option = option,
352 workplace = workplace,
353 bias = 'workplace',
354 default = defaults
355 )
356
357 if p_list is not None:
358 return p_list
359
360 if defaults is None:
361 p_list = get_installed_plugins(plugin_dir = plugin_dir)
362 if (len(p_list) == 0):
363 _log.error('cannot find plugins by scanning plugin directory ?!?')
364 return defaults
365 else:
366 p_list = defaults
367
368
369 dbcfg.set (
370 option = option,
371 value = p_list,
372 workplace = workplace
373 )
374
375 _log.debug("plugin load list stored: %s" % str(p_list))
376 return p_list
377
385
386
387
388 if __name__ == '__main__':
389
390 if len(sys.argv) > 1 and sys.argv[1] == 'test':
391 print get_installed_plugins('gui')
392
393
394