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