| Home | Trees | Indices | Help |
|
|---|
|
|
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 #----------------------------------------------------------
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 # This is for NOTEBOOK plugins. Please write other base
57 # classes for other types of plugins.
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 # plugin load API
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 # create widget
76 nb = self.gb['horstspace.notebook']
77 widget = self.GetWidget(nb)
78
79 # create toolbar
80 #top_panel = self.gb['horstspace.top_panel']
81 #tb = top_panel.CreateBar()
82 #self.populate_toolbar(tb, widget)
83 #tb.Realize()
84 # place bar in top panel
85 # (pages that don't want a toolbar must install a blank one
86 # otherwise the previous page's toolbar would be visible)
87 #top_panel.AddBar(key=self.__class__.__name__, bar=tb)
88 #self.gb['toolbar.%s' % self.__class__.__name__] = tb
89
90 # add ourselves to the main notebook
91 nb.AddPage(widget, self.name())
92
93 # so notebook can find this widget
94 self.gb['horstspace.notebook.%s' % self._set][self.__class__.__name__] = self
95 self.gb['horstspace.notebook.pages'].append(self)
96
97 # and put ourselves into the menu structure
98 menu_info = self.MenuInfo()
99 if menu_info is None:
100 # register with direct access menu only
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 # FIXME: this shouldn't be self.name() but rather self.menu_help_string()
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 # delete menu item
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 # delete toolbar
128 #top_panel = self.gb['main.top_panel']
129 #top_panel.DeleteBar(self.__class__.__name__)
130
131 # correct the notebook page list
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 # delete notebook page
137 nb = self.gb['horstspace.notebook']
138 nb.DeletePage(nb_page_num)
139 #-----------------------------------------------------
142 #-----------------------------------------------------
144 """Return tuple of (menuname, menuitem).
145
146 None: no menu entry wanted
147 """
148 return None
149 #-----------------------------------------------------
150 # def populate_toolbar (self, tb, widget):
151 # """Populates the toolbar for this widget.
152 #
153 # - tb is the toolbar to populate
154 # - widget is the widget returned by GetWidget() # FIXME: is this really needed ?
155 # """
156 # pass
157 #-----------------------------------------------------
158 # activation API
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 # FIXME: fail if locked
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 # else apparently it doesn't need it
177 return True
178 #-----------------------------------------------------
180 """Check for patient availability.
181
182 - convenience method for your can_receive_focus() handlers
183 """
184 # fail if no patient selected
185 pat = gmPerson.gmCurrentPatient()
186 if not pat.connected:
187 # FIXME: people want an optional red backgound here
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 # does this signal concern us ?
208 if kwds['name'] not in [self.__class__.__name__, self.name()]:
209 return False
210 return self._on_raise_by_menu(None)
211 # -----------------------------------------------------
212 # event handlers for the popup window
216 # FIXME: raise ?
217 # -----------------------------------------------------
219 self.register() # register without changing configuration
220 # -----------------------------------------------------
223 #==================================================================
225 """This mixin adds listening to patient change signals."""
227 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection')
228 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
229 # -----------------------------------------------------
231 print "%s._pre_patient_selection() not implemented" % self.__class__.__name__
232 print "should usually be used to commit unsaved data"
233 # -----------------------------------------------------
237 #==================================================================
238 # some convenience functions
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 # we do need brokers, else we are useless
273 gb = gmGuiBroker.GuiBroker()
274
275 # bean counting ! -> loaded plugins
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 # find name of class of plugin (must be the same as the plugin module filename)
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 # store for current user/current workplace
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 #------------------------------------------------------------------
377 """
378 Unloads the named plugin
379 """
380 gb = gmGuiBroker.GuiBroker()
381 plugin = gb['horstspace.notebook.%s' % set][name]
382 plugin.unregister()
383 #==================================================================
384 # Main
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
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Jun 25 03:58:46 2012 | http://epydoc.sourceforge.net |