| Trees | Indices | Help |
|
|---|
|
|
1 """GnuMed Horst-space inner-frame layout manager.
2
3 This implements the simple wx.Notebook based layout as
4 originally suggested by Horst Herb.
5
6 This source code is protected by the GPL licensing scheme.
7 Details regarding the GPL are available at http://www.gnu.org
8 You may use and share it as long as you don't deny this right
9 to anybody else.
10
11 copyright: authors
12 """
13 #==============================================================================
14 # $Source: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmHorstSpace.py,v $
15 # $Id: gmHorstSpace.py,v 1.47 2008/12/09 23:30:38 ncq Exp $
16 __version__ = "$Revision: 1.47 $"
17 __author__ = "H. Herb <hherb@gnumed.net>,\
18 K. Hilbert <Karsten.Hilbert@gmx.net>,\
19 I. Haywood <i.haywood@ugrad.unimelb.edu.au>"
20 __license__ = 'GPL (details at http://www.gnu.org)'
21
22 import os.path, os, sys, logging
23
24
25 import wx
26
27
28 from Gnumed.pycommon import gmGuiBroker, gmI18N, gmDispatcher, gmCfg
29 from Gnumed.wxpython import gmPlugin, gmTopPanel, gmGuiHelpers
30 from Gnumed.business import gmPerson, gmSurgery
31
32
33 _log = logging.getLogger('gm.ui')
34 _log.info(__version__)
35 #==============================================================================
36 # finding the visible page from a notebook page: self.GetParent.GetCurrentPage == self
38 """GnuMed inner-frame layout manager.
39
40 This implements a Horst-space notebook-only
41 "inner-frame" layout manager.
42 """
44 # main panel
45 wx.Panel.__init__(
46 self,
47 parent = parent,
48 id = id,
49 pos = wx.DefaultPosition,
50 size = wx.DefaultSize,
51 style = wx.NO_BORDER,
52 name = 'HorstSpace.LayoutMgrPnl'
53 )
54 # notebook
55 self.nb = wx.Notebook (
56 parent=self,
57 id = -1,
58 size = wx.Size(320,240),
59 style = wx.NB_BOTTOM
60 )
61 # plugins
62 self.__gb = gmGuiBroker.GuiBroker()
63 self.__gb['horstspace.notebook'] = self.nb # FIXME: remove per Ian's API suggestion
64
65 # top panel
66 #---------------------
67 # create the "top row"
68 #---------------------
69 # important patient data is always displayed there
70 # - top panel with toolbars
71 self.top_panel = gmTopPanel.cMainTopPanel(self, -1)
72 self.__gb['horstspace.top_panel'] = self.top_panel
73 self.__load_plugins()
74
75 # layout handling
76 self.main_szr = wx.BoxSizer(wx.VERTICAL)
77 self.main_szr.Add(self.top_panel, 0, wx.EXPAND)
78 self.main_szr.Add(self.nb, 1, wx.EXPAND)
79 self.SetSizer(self.main_szr)
80 # self.SetSizerAndFit(self.main_szr)
81 # self.Layout()
82 # self.Show(True)
83
84 self.__register_events()
85 #----------------------------------------------
86 # internal API
87 #----------------------------------------------
89 # - notebook page is about to change
90 self.nb.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self._on_notebook_page_changing)
91 # - notebook page has been changed
92 self.nb.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self._on_notebook_page_changed)
93 # - popup menu on right click in notebook
94 wx.EVT_RIGHT_UP(self.nb, self._on_right_click)
95
96 gmDispatcher.connect(self._on_post_patient_selection, u'post_patient_selection')
97 #----------------------------------------------
99 # get plugin list
100 plugin_list = gmPlugin.GetPluginLoadList (
101 option = 'horstspace.notebook.plugin_load_order',
102 plugin_dir = 'gui',
103 defaults = ['gmProviderInboxPlugin']
104 )
105
106 nr_plugins = len(plugin_list)
107 wx.BeginBusyCursor()
108
109 # set up a progress bar
110 progress_bar = gmPlugin.cLoadProgressBar(nr_plugins)
111
112 # and load them
113 prev_plugin = ""
114 first_plugin = None
115 plugin = None
116 result = -1
117 for idx in range(nr_plugins):
118 curr_plugin = plugin_list[idx]
119 progress_bar.Update(result, curr_plugin)
120 try:
121 plugin = gmPlugin.instantiate_plugin('gui', curr_plugin)
122 if plugin:
123 plugin.register()
124 result = 1
125 else:
126 _log.error("plugin [%s] not loaded, see errors above", curr_plugin)
127 result = 1
128 except:
129 _log.exception('failed to load plugin %s', curr_plugin)
130 result = 0
131
132 if first_plugin is None:
133 first_plugin = plugin
134 prev_plugin = curr_plugin
135
136 progress_bar.Destroy()
137 wx.EndBusyCursor()
138
139 # force-refresh first notebook page
140 page = self.nb.GetPage(0)
141 page.Refresh()
142
143 return True
144 #----------------------------------------------
145 # external callbacks
146 #----------------------------------------------
148 db_cfg = gmCfg.cCfgSQL()
149 default_plugin = db_cfg.get2 (
150 option = u'patient_search.plugin_to_raise_after_search',
151 workplace = gmSurgery.gmCurrentPractice().active_workplace,
152 bias = u'user',
153 default = u'gmEMRBrowserPlugin'
154 )
155 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = default_plugin)
156 #----------------------------------------------
158 """Called before notebook page change is processed."""
159
160 _log.debug('just before switching notebook tabs')
161
162 self.__new_page_already_checked = False
163
164 self.__id_nb_page_before_switch = self.nb.GetSelection()
165 self.__id_evt_page_before_switch = event.GetOldSelection()
166 __id_evt_page_after_switch = event.GetSelection()
167
168 _log.debug('event.GetOldSelection()=%s* -> event.GetSelection()=%s', self.__id_evt_page_before_switch, __id_evt_page_after_switch)
169
170 if self.__id_evt_page_before_switch != self.__id_nb_page_before_switch:
171 _log.debug('the following two should match but do not:')
172 _log.debug(' event.GetOldSelection(): %s', self.__id_evt_page_before_switch)
173 _log.debug(' notebook.GetSelection(): %s', self.__id_nb_page_before_switch)
174
175 # can we check the target page ?
176 if __id_evt_page_after_switch == self.__id_evt_page_before_switch:
177 # no, so complain
178 # (the docs say that on Windows GetSelection() returns the
179 # old page ID, eg. the same value GetOldSelection() returns)
180 _log.debug('this system is: sys: [%s] wx: [%s]', sys.platform, wx.Platform)
181 _log.debug('it seems to be one of those platforms that have no clue which notebook page they are switching to')
182 _log.debug('(Windows is documented to return the old page from both evt.GetOldSelection() and evt.GetSelection())')
183 _log.debug('current notebook page : %s', self.__id_nb_page_before_switch)
184 _log.debug('source page from event: %s', self.__id_evt_page_before_switch)
185 _log.debug('target page from event: %s', __id_evt_page_after_switch)
186 _log.info('cannot check whether notebook page change needs to be vetoed')
187 # but let's do a basic check anyways
188 pat = gmPerson.gmCurrentPatient()
189 if not pat.connected:
190 gmDispatcher.send(signal = 'statustext', msg =_('Cannot change notebook tabs. No active patient.'))
191 event.Veto()
192 return
193 # that test passed, so let's hope things are fine
194 event.Allow() # redundant ?
195 event.Skip()
196 return
197
198 # check target page
199 new_page = self.__gb['horstspace.notebook.pages'][__id_evt_page_after_switch]
200 if not new_page.can_receive_focus():
201 _log.debug('veto()ing page change')
202 event.Veto()
203 return
204
205 # everything seems fine so switch
206 self.__new_page_already_checked = True
207 event.Allow() # redundant ?
208 event.Skip()
209 return
210 #----------------------------------------------
212 """Called when notebook page changes."""
213
214 _log.debug('just after switching notebook tabs')
215
216 id_evt_page_before_switch = event.GetOldSelection()
217 id_evt_page_after_switch = event.GetSelection()
218 id_nb_page_after_switch = self.nb.GetSelection()
219
220 _log.debug('event.GetOldSelection()=%s -> event.GetSelection()=%s*', id_evt_page_before_switch, id_evt_page_after_switch)
221
222 if self.__id_nb_page_before_switch != id_evt_page_before_switch:
223 _log.debug('those two really *should* match:')
224 _log.debug(' wx.Notebook.GetSelection() (before switch) : %s' % self.__id_nb_page_before_switch)
225 _log.debug(' EVT_NOTEBOOK_PAGE_CHANGED.GetOldSelection(): %s' % id_evt_page_before_switch)
226
227 new_page = self.__gb['horstspace.notebook.pages'][id_evt_page_after_switch]
228
229 # well-behaving wxPython port ?
230 if self.__new_page_already_checked:
231 new_page.receive_focus()
232 # activate toolbar of new page
233 # self.__gb['horstspace.top_panel'].ShowBar(new_page.__class__.__name__)
234 self.__new_page_already_checked = False
235 event.Skip()
236 return
237
238 # no, complain
239 _log.debug('target page not checked for focussability yet')
240 _log.debug('EVT_NOTEBOOK_PAGE_CHANGED.GetOldSelection(): %s' % id_evt_page_before_switch)
241 _log.debug('EVT_NOTEBOOK_PAGE_CHANGED.GetSelection() : %s' % id_evt_page_after_switch)
242 _log.debug('wx.Notebook.GetSelection() (after switch) : %s' % id_nb_page_after_switch)
243
244 # check the new page just for good measure
245 if new_page.can_receive_focus():
246 _log.debug('we are lucky: new page *can* receive focus')
247 new_page.receive_focus()
248 # activate toolbar of new page
249 # self.__gb['horstspace.top_panel'].ShowBar(new_page.__class__.__name__)
250 event.Skip()
251 return
252
253 _log.warning('new page cannot receive focus but too late for veto')
254 event.Skip()
255 return
256 #----------------------------------------------
258 evt.Skip()
259 return
260
261 load_menu = wx.Menu()
262 any_loadable = 0
263 plugin_list = gmPlugin.GetPluginLoadList('gui')
264 plugin = None
265 for plugin_name in plugin_list:
266 try:
267 plugin = gmPlugin.instantiate_plugin('gui', plugin_name)
268 except StandardError:
269 continue
270 # not a plugin
271 if not isinstance(plugin, gmPlugin.cNotebookPlugin):
272 plugin = None
273 continue
274 # already loaded ?
275 if plugin.__class__.__name__ in self.guibroker['horstspace.notebook.gui'].keys():
276 plugin = None
277 continue
278 # add to load menu
279 nid = wx.NewId()
280 load_menu.AppendItem(wx.MenuItem(load_menu, nid, plugin.name()))
281 wx.EVT_MENU(load_menu, nid, plugin.on_load)
282 any_loadable = 1
283 # make menus
284 menu = wx.Menu()
285 ID_LOAD = wx.NewId()
286 ID_DROP = wx.NewId()
287 if any_loadable:
288 menu.AppendMenu(ID_LOAD, _('add plugin ...'), load_menu)
289 plugins = self.guibroker['horstspace.notebook.gui']
290 raised_plugin = plugins[self.nb.GetSelection()].name()
291 menu.AppendItem(wx.MenuItem(menu, ID_DROP, "drop [%s]" % raised_plugin))
292 wx.EVT_MENU (menu, ID_DROP, self._on_drop_plugin)
293 self.PopupMenu(menu, evt.GetPosition())
294 menu.Destroy()
295 evt.Skip()
296 #----------------------------------------------
298 """Unload plugin and drop from load list."""
299 pages = self.guibroker['horstspace.notebook.pages']
300 page = pages[self.nb.GetSelection()]
301 page.unregister()
302 self.nb.AdvanceSelection()
303 # FIXME:"dropping" means talking to configurator so not reloaded
304 #----------------------------------------------
306 """Unload plugin but don't touch configuration."""
307 # this dictionary links notebook page numbers to plugin objects
308 pages = self.guibroker['horstspace.notebook.pages']
309 page = pages[self.nb.GetSelection()]
310 page.unregister()
311 #==============================================================================
312 if __name__ == '__main__':
313 wx.InitAllImageHandlers()
314 pgbar = gmPluginLoadProgressBar(3)
315
316 #==============================================================================
317 # $Log: gmHorstSpace.py,v $
318 # Revision 1.47 2008/12/09 23:30:38 ncq
319 # - make raising plugin after patient activation thread safe
320 #
321 # Revision 1.46 2008/11/20 20:06:12 ncq
322 # - cleanup
323 #
324 # Revision 1.45 2008/07/10 11:20:27 ncq
325 # - no more toolbar handling
326 #
327 # Revision 1.44 2008/04/02 10:46:14 ncq
328 # - better logging
329 #
330 # Revision 1.43 2008/03/29 16:11:10 ncq
331 # - improve logging of notebook page change events yet again
332 #
333 # Revision 1.42 2008/03/06 18:29:29 ncq
334 # - standard lib logging only
335 #
336 # Revision 1.41 2008/01/30 14:03:42 ncq
337 # - use signal names directly
338 # - switch to std lib logging
339 #
340 # Revision 1.40 2007/10/08 12:50:54 ncq
341 # - active_workplace now in gmPractice()
342 #
343 # Revision 1.39 2007/08/28 14:18:13 ncq
344 # - no more gm_statustext()
345 #
346 # Revision 1.38 2007/08/12 00:09:07 ncq
347 # - no more gmSignals.py
348 #
349 # Revision 1.37 2007/02/17 14:13:11 ncq
350 # - gmPerson.gmCurrentProvider().workplace now property
351 #
352 # Revision 1.36 2007/02/15 14:57:49 ncq
353 # - cleanup
354 #
355 # Revision 1.35 2006/12/17 20:44:52 ncq
356 # - cleanup
357 #
358 # Revision 1.34 2006/11/24 10:01:31 ncq
359 # - gm_beep_statustext() -> gm_statustext()
360 #
361 # Revision 1.33 2006/11/07 00:34:16 ncq
362 # - cleanup
363 # - raise configured plugin after successful patient search
364 #
365 # Revision 1.32 2006/10/08 11:04:09 ncq
366 # - add sensible default for plugin load list
367 # - robustify __load_plugins() somewhat
368 #
369 # Revision 1.31 2006/06/18 21:55:22 ncq
370 # - better variable naming in page change handlers, again
371 #
372 # Revision 1.30 2006/06/18 13:24:27 ncq
373 # - use Bind() instead of event binding macros
374 # - improved page change logging, all to no avail
375 #
376 # Revision 1.29 2006/05/28 15:45:52 ncq
377 # - cleanup page activation code and reinit already_checked helper var
378 #
379 # Revision 1.28 2006/05/20 18:37:10 ncq
380 # - cleanup
381 #
382 # Revision 1.27 2006/05/15 13:36:49 ncq
383 # - cleanup
384 #
385 # Revision 1.26 2006/05/12 12:18:11 ncq
386 # - whoami -> whereami cleanup
387 # - use gmCurrentProvider()
388 #
389 # Revision 1.25 2006/05/10 13:09:57 ncq
390 # - improved error logging in notebook page switching
391 #
392 # Revision 1.24 2005/12/27 18:57:29 ncq
393 # - better document Syan's workaround
394 #
395 # Revision 1.23 2005/12/26 08:57:26 sjtan
396 #
397 # repaint may not be signalled on some platforms ( gtk ? ); repaint occurs if 1) the emrbrowser is the selected notebook page AND
398 # 2) the frame is re-sized. This suggests repaint is best done on notebook page changed. This workaround goes to
399 # the demographic page on a new patient select - let's the user confirm they have selected the right patient; then when
400 # switch to emrbrowser, this signals data_reget. seems to work.
401 #
402 # Revision 1.22 2005/09/28 21:21:35 ncq
403 # - non-initialized variable plugin in plugin loading
404 # - wx2.6 fixing
405 #
406 # Revision 1.21 2005/09/28 15:57:48 ncq
407 # - a whole bunch of wx.Foo -> wx.Foo
408 #
409 # Revision 1.20 2005/09/27 20:44:59 ncq
410 # - wx.wx* -> wx.*
411 #
412 # Revision 1.19 2005/09/26 18:01:51 ncq
413 # - use proper way to import wx26 vs wx2.4
414 # - note: THIS WILL BREAK RUNNING THE CLIENT IN SOME PLACES
415 # - time for fixup
416 #
417 # Revision 1.18 2005/09/25 17:32:50 ncq
418 # - revert back to 2.4 wx import until compatible 2.6 method is found
419 #
420 # Revision 1.17 2005/09/25 01:00:47 ihaywood
421 # bugfixes
422 #
423 # remember 2.6 uses "import wx" not "from wxPython import wx"
424 # removed not null constraint on clin_encounter.rfe as has no value on instantiation
425 # client doesn't try to set clin_encounter.description as it doesn't exist anymore
426 #
427 # Revision 1.16 2005/09/24 09:17:29 ncq
428 # - some wx2.6 compatibility fixes
429 #
430 # Revision 1.15 2005/07/31 16:22:57 ncq
431 # - cleanup
432 #
433 # Revision 1.14 2005/07/23 22:03:08 shilbert
434 # - yet another typo
435 #
436 # Revision 1.13 2005/07/23 21:44:21 shilbert
437 # - silly typo
438 #
439 # Revision 1.12 2005/07/23 19:08:36 ncq
440 # - robust detection of lossy notebook tab switching wxPython code
441 #
442 # Revision 1.11 2005/07/21 16:21:01 ncq
443 # - log everything there is to know about changing notebook tabs,
444 # debugging on Windows is in order
445 #
446 # Revision 1.10 2005/07/19 17:06:35 ncq
447 # - try again to make windows behave regarding notebook tab switching
448 #
449 # Revision 1.9 2005/07/18 20:47:41 ncq
450 # - try to improve notebook page changing trick
451 # needed on Windows
452 #
453 # Revision 1.8 2005/02/01 19:20:23 ncq
454 # - just silly cleanup
455 #
456 # Revision 1.7 2005/02/01 10:16:07 ihaywood
457 # refactoring of gmDemographicRecord and follow-on changes as discussed.
458 #
459 # gmTopPanel moves to gmHorstSpace
460 # gmRichardSpace added -- example code at present, haven't even run it myself
461 # (waiting on some icon .pngs from Richard)
462 #
463 # Revision 1.6 2004/10/17 16:06:30 ncq
464 # - silly whitespace fix
465 #
466 # Revision 1.5 2004/10/16 22:42:12 sjtan
467 #
468 # script for unitesting; guard for unit tests where unit uses gmPhraseWheel; fixup where version of wxPython doesn't allow
469 # a child widget to be multiply inserted (gmDemographics) ; try block for later versions of wxWidgets that might fail
470 # the Add (.. w,h, ... ) because expecting Add(.. (w,h) ...)
471 #
472 # Revision 1.4 2004/10/14 12:11:18 ncq
473 # - improve comments
474 #
475 # Revision 1.3 2004/09/13 08:53:02 ncq
476 # - gmMacroPrimitives.raise_notebook_plugin() didn't work since
477 # cHorstSpaceLayoutMgr used guibroker['horstspace.plugins'] rather
478 # than 'horstspace.notebook.gui'
479 #
480 # Revision 1.2 2004/08/18 08:17:40 ncq
481 # - wxMac workaround for missing wxIcon.LoadFile()
482 #
483 # Revision 1.1 2004/08/08 23:54:37 ncq
484 # - factored out Horst space layout manager
485 #
486
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Feb 9 04:02:37 2010 | http://epydoc.sourceforge.net |