Package Gnumed :: Package wxpython :: Module gmHorstSpace
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmHorstSpace

  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 
37 -class cHorstSpaceLayoutMgr(wx.Panel):
38 """GnuMed inner-frame layout manager. 39 40 This implements a Horst-space notebook-only 41 "inner-frame" layout manager. 42 """
43 - def __init__(self, parent, id):
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 #----------------------------------------------
88 - def __register_events(self):
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 #----------------------------------------------
98 - def __load_plugins(self):
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 #----------------------------------------------
147 - def _on_post_patient_selection(self, **kwargs):
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 #----------------------------------------------
157 - def _on_notebook_page_changing(self, event):
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 #----------------------------------------------
211 - def _on_notebook_page_changed(self, event):
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 #----------------------------------------------
257 - def _on_right_click(self, evt):
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 #----------------------------------------------
297 - def _on_drop_plugin(self, evt):
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 #----------------------------------------------
305 - def _on_hide_plugin (self, evt):
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