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  copyright: authors 
  7  """ 
  8  #============================================================================== 
  9  __author__  = "H. Herb <hherb@gnumed.net>,\ 
 10                             K. Hilbert <Karsten.Hilbert@gmx.net>,\ 
 11                             I. Haywood <i.haywood@ugrad.unimelb.edu.au>" 
 12  __license__ = 'GPL v2 or later (details at http://www.gnu.org)' 
 13   
 14  import os.path, os, sys, logging 
 15   
 16   
 17  import wx 
 18   
 19   
 20  from Gnumed.pycommon import gmGuiBroker, gmI18N, gmDispatcher, gmCfg, gmLog2 
 21  from Gnumed.wxpython import gmPlugin, gmTopPanel, gmGuiHelpers 
 22  from Gnumed.business import gmPerson, gmPraxis 
 23   
 24   
 25  _log = logging.getLogger('gm.ui') 
 26   
 27  #============================================================================== 
28 -class cHorstSpaceNotebook(wx.Notebook): # wx.BestBook ?
29
30 - def __init__(self, *args, **kwargs):
31 32 kwargs['style'] = wx.NB_BOTTOM 33 kwargs['id'] = -1 34 wx.Notebook.__init__(self, *args, **kwargs) 35 36 _log.debug('created wx.Notebook: %s with ID %s', self.__class__.__name__, self.Id)
37 38 #self.__register_events() 39 40 #---------------------------------------------- 41 # internal API 42 #----------------------------------------------
43 - def __register_events(self):
44 # because of 45 # https://www.wiki.wxpython.org/self.Bind%20vs.%20self.button.Bind 46 # do self.Bind() rather than self.nb.Bind() 47 # - notebook page is about to change 48 #self.nb.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self._on_notebook_page_changing) 49 #self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self._on_notebook_page_changing, self) 50 self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self._on_notebook_page_changing) 51 # - notebook page has been changed 52 #self.nb.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self._on_notebook_page_changed) 53 #self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self._on_notebook_page_changed, self) 54 self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self._on_notebook_page_changed)
55 # - popup menu on right click in notebook 56 #wx.EVT_RIGHT_UP(self.nb, self._on_right_click) 57 58 #---------------------------------------------- 59 # event handlers 60 #----------------------------------------------
61 - def _on_notebook_page_changing(self, event):
62 """Called before notebook page change is processed.""" 63 64 _log.debug('just before switching notebook tabs') 65 66 _log.debug('id: %s', event.Id) 67 _log.debug('event object (= source notebook): %s = %s', event.EventObject.Id, event.EventObject) 68 _log.debug('this notebook (= event receiver): %s = %s', self.Id, self) 69 if event.EventObject.Id != self.Id: 70 _log.error('this event came from another notebook') 71 72 self.__target_page_already_checked = False 73 74 self.__id_nb_page_before_switch = self.GetSelection() 75 self.__id_evt_page_before_switch = event.GetOldSelection() 76 __id_evt_page_after_switch = event.GetSelection() 77 78 _log.debug('source/target page state in EVT_NOTEBOOK_PAGE_CHANGING:') 79 _log.debug(' #1 - notebook current page: %s (= notebook.GetSelection())', self.__id_nb_page_before_switch) 80 _log.debug(' #2 - event source page: %s (= page event says it is coming from, event.GetOldSelection())', self.__id_evt_page_before_switch) 81 _log.debug(' #3 - event target page: %s (= page event wants to go to, event.GetSelection())', __id_evt_page_after_switch) 82 if self.__id_evt_page_before_switch != self.__id_nb_page_before_switch: 83 _log.warning(' problem: #1 and #2 really should match but do not') 84 85 # can we check the target page ? 86 if __id_evt_page_after_switch == self.__id_evt_page_before_switch: 87 # no, so complain 88 # (the docs say that on Windows GetSelection() returns the 89 # old page ID, eg. the same value GetOldSelection() returns) 90 _log.debug('this system is: sys: [%s] wx: [%s]', sys.platform, wx.Platform) 91 _log.debug('it seems to be one of those platforms that have no clue which notebook page they are switching to') 92 _log.debug('(Windows is documented to return the old page from both evt.GetOldSelection() and evt.GetSelection())') 93 _log.debug('current notebook page : %s', self.__id_nb_page_before_switch) 94 _log.debug('source page from event: %s', self.__id_evt_page_before_switch) 95 _log.debug('target page from event: %s', __id_evt_page_after_switch) 96 _log.warning('cannot check whether notebook page change needs to be vetoed') 97 # but let's do a basic check anyways 98 pat = gmPerson.gmCurrentPatient() 99 if not pat.connected: 100 gmDispatcher.send(signal = 'statustext', msg =_('Cannot change notebook tabs. No active patient.')) 101 event.Veto() 102 return 103 # that test passed, so let's hope things are fine 104 event.Allow() # redundant ? 105 event.Skip() 106 return 107 108 # check target page 109 target_page = self.__gb['horstspace.notebook.pages'][__id_evt_page_after_switch] 110 _log.debug('checking event target page for focussability: %s', target_page) 111 if not target_page.can_receive_focus(): 112 _log.warning('veto()ing page change') 113 event.Veto() 114 return 115 116 # everything seems fine so switch 117 _log.debug('event target page seems focussable') 118 self.__target_page_already_checked = True 119 event.Allow() # redundant ? 120 event.Skip() 121 return
122 123 #----------------------------------------------
124 - def _on_notebook_page_changed(self, event):
125 """Called when notebook page changes.""" 126 127 _log.debug('just after switching notebook tabs') 128 129 _log.debug('id: %s', event.Id) 130 _log.debug('event object (= source notebook): %s = %s', event.EventObject.Id, event.EventObject) 131 _log.debug('this notebook (= event receiver): %s = %s', self.Id, self) 132 if event.EventObject.Id != self.Id: 133 _log.error('this event came from another notebook') 134 135 event.Skip() 136 137 id_nb_page_after_switch = self.GetSelection() 138 id_evt_page_before_switch = event.GetOldSelection() 139 id_evt_page_after_switch = event.GetSelection() 140 141 _log.debug('source/target page state in EVT_NOTEBOOK_PAGE_CHANGED:') 142 _log.debug(' #1 - current notebook page: %s (notebook.GetSelection())', id_nb_page_after_switch) 143 _log.debug(' #2 - event source page: %s (= page event says it is coming from, event.GetOldSelection())', id_evt_page_before_switch) 144 _log.debug(' #3 - event target page: %s (= page event wants to go to, event.GetSelection())', id_evt_page_after_switch) 145 146 if self.__id_nb_page_before_switch != id_evt_page_before_switch: 147 _log.warning('those two really *should* match:') 148 _log.warning(' wx.Notebook.GetSelection(): %s (notebook current page before switch) ', self.__id_nb_page_before_switch) 149 _log.warning(' EVT_NOTEBOOK_PAGE_CHANGED.GetOldSelection(): %s (event source page)' % id_evt_page_before_switch) 150 151 target_page = self.__gb['horstspace.notebook.pages'][id_evt_page_after_switch] 152 153 # well-behaving wxPython port ? 154 if self.__target_page_already_checked: 155 _log.debug('target page (evt=%s, nb=%s) claims to have been checked for focussability already: %s', id_evt_page_after_switch, id_nb_page_after_switch, target_page) 156 target_page.receive_focus() 157 self.__target_page_already_checked = False 158 return 159 160 # no, complain 161 _log.debug('target page not checked for focussability yet: %s', target_page) 162 _log.debug('EVT_NOTEBOOK_PAGE_CHANGED.GetOldSelection(): %s' % id_evt_page_before_switch) 163 _log.debug('EVT_NOTEBOOK_PAGE_CHANGED.GetSelection() : %s' % id_evt_page_after_switch) 164 _log.debug('wx.Notebook.GetSelection() (after switch) : %s' % id_nb_page_after_switch) 165 166 # check the new page just for good measure 167 if target_page.can_receive_focus(): 168 _log.debug('we are lucky: target page *can* receive focus anyway') 169 target_page.receive_focus() 170 return 171 172 _log.error('target page cannot receive focus but too late for veto') 173 return
174 175 #============================================================================== 176 # finding the visible page from a notebook page: self.GetParent.GetCurrentPage == self
177 -class cHorstSpaceLayoutMgr(wx.Panel):
178 """GNUmed inner-frame layout manager. 179 180 This implements a Horst-space notebook-only 181 "inner-frame" layout manager. 182 """
183 - def __init__(self, parent, id):
184 # main panel 185 wx.Panel.__init__( 186 self, 187 parent = parent, 188 id = id, 189 pos = wx.DefaultPosition, 190 size = wx.DefaultSize, 191 style = wx.NO_BORDER, 192 name = 'HorstSpace.LayoutMgrPnl' 193 ) 194 195 # top "row": important patient data is displayed there continually 196 self.top_panel = gmTopPanel.cTopPnl(self, -1) 197 198 # notebook 199 self.nb = cHorstSpaceNotebook(parent = self) 200 201 # plugins 202 self.__gb = gmGuiBroker.GuiBroker() 203 self.__gb['horstspace.top_panel'] = self.top_panel 204 self.__gb['horstspace.notebook'] = self.nb # FIXME: remove per Ian's API suggestion 205 self.__load_plugins() 206 207 # layout handling 208 self.main_szr = wx.BoxSizer(wx.VERTICAL) 209 self.main_szr.Add(self.top_panel, 0, wx.EXPAND) 210 self.main_szr.Add(self.nb, 1, wx.EXPAND) 211 self.SetSizer(self.main_szr) 212 # self.SetSizerAndFit(self.main_szr) 213 # self.Layout() 214 # self.Show(True) 215 216 self.__register_events()
217 218 #---------------------------------------------- 219 # internal API 220 #----------------------------------------------
221 - def __register_events(self):
222 # # because of 223 # # https://www.wiki.wxpython.org/self.Bind%20vs.%20self.button.Bind 224 # # do self.Bind() rather than self.nb.Bind() 225 # # - notebook page is about to change 226 # #self.nb.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self._on_notebook_page_changing) 227 self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self._on_notebook_page_changing, self.nb) 228 # # - notebook page has been changed 229 # #self.nb.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self._on_notebook_page_changed) 230 self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self._on_notebook_page_changed, self.nb) 231 # # - popup menu on right click in notebook 232 # #wx.EVT_RIGHT_UP(self.nb, self._on_right_click) 233 234 gmDispatcher.connect(self._on_post_patient_selection, 'post_patient_selection')
235 236 #----------------------------------------------
237 - def __load_plugins(self):
238 239 wx.BeginBusyCursor() 240 241 # get plugin list 242 plugins2load_by_name = gmPlugin.GetPluginLoadList ( 243 option = 'horstspace.notebook.plugin_load_order', 244 plugin_dir = 'gui', 245 defaults = ['gmProviderInboxPlugin'] 246 ) 247 248 _log.debug('plugin load order: %s', plugins2load_by_name) 249 250 nr_plugins = len(plugins2load_by_name) 251 failed_plugins = [] 252 253 # set up a progress bar 254 progress_bar = gmPlugin.cLoadProgressBar(nr_plugins) 255 256 # and load them 257 prev_plugin = "" 258 first_plugin = None 259 plugin = None 260 result = -1 261 for idx in range(nr_plugins): 262 curr_plugin = plugins2load_by_name[idx] 263 progress_bar.Update(result, curr_plugin) 264 try: 265 plugin = gmPlugin.instantiate_plugin('gui', curr_plugin) 266 if plugin: 267 plugin.register() 268 result = 1 269 else: 270 _log.error("plugin [%s] not loaded, see errors above", curr_plugin) 271 failed_plugins.append(curr_plugin) 272 result = 1 273 except: 274 _log.exception('failed to load plugin %s', curr_plugin) 275 failed_plugins.append(curr_plugin) 276 result = 0 277 278 if first_plugin is None: 279 first_plugin = plugin 280 prev_plugin = curr_plugin 281 282 _log.debug('failed plugins: %s', failed_plugins) 283 progress_bar.Destroy() 284 wx.EndBusyCursor() 285 286 # force-refresh first notebook page 287 page = self.nb.GetPage(0) 288 page.Refresh() 289 290 return True
291 292 #---------------------------------------------- 293 # external callbacks 294 #----------------------------------------------
295 - def _on_post_patient_selection(self, **kwargs):
296 db_cfg = gmCfg.cCfgSQL() 297 default_plugin = db_cfg.get2 ( 298 option = 'patient_search.plugin_to_raise_after_search', 299 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 300 bias = 'user', 301 default = 'gmPatientOverviewPlugin' 302 ) 303 gmDispatcher.send(signal = 'display_widget', name = default_plugin)
304 305 #----------------------------------------------
306 - def _on_notebook_page_changing(self, event):
307 """Called before notebook page change is processed.""" 308 309 _log.debug('just before switching notebook tabs') 310 311 _log.debug('id: %s', event.Id) 312 _log.debug('event object (= source notebook): %s = %s', event.EventObject.Id, event.EventObject) 313 _log.debug('this notebook (= event receiver): %s = %s', self.nb.Id, self.nb) 314 if event.EventObject.Id != self.nb.Id: 315 _log.error('this event came from another notebook') 316 317 self.__target_page_already_checked = False 318 319 self.__id_nb_page_before_switch = self.nb.GetSelection() 320 self.__id_evt_page_before_switch = event.GetOldSelection() 321 __id_evt_page_after_switch = event.GetSelection() 322 323 _log.debug('source/target page state in EVT_NOTEBOOK_PAGE_CHANGING:') 324 _log.debug(' #1 - notebook current page: %s (= notebook.GetSelection())', self.__id_nb_page_before_switch) 325 _log.debug(' #2 - event source page: %s (= page event says it is coming from, event.GetOldSelection())', self.__id_evt_page_before_switch) 326 _log.debug(' #3 - event target page: %s (= page event wants to go to, event.GetSelection())', __id_evt_page_after_switch) 327 if self.__id_evt_page_before_switch != self.__id_nb_page_before_switch: 328 _log.warning(' problem: #1 and #2 really should match but do not') 329 330 # can we check the target page ? 331 if __id_evt_page_after_switch == self.__id_evt_page_before_switch: 332 # no, so complain 333 # (the docs say that on Windows GetSelection() returns the 334 # old page ID, eg. the same value GetOldSelection() returns) 335 _log.debug('this system is: sys: [%s] wx: [%s]', sys.platform, wx.Platform) 336 _log.debug('it seems to be one of those platforms that have no clue which notebook page they are switching to') 337 _log.debug('(Windows is documented to return the old page from both evt.GetOldSelection() and evt.GetSelection())') 338 _log.debug('current notebook page : %s', self.__id_nb_page_before_switch) 339 _log.debug('source page from event: %s', self.__id_evt_page_before_switch) 340 _log.debug('target page from event: %s', __id_evt_page_after_switch) 341 _log.warning('cannot check whether notebook page change needs to be vetoed') 342 # but let's do a basic check anyways 343 pat = gmPerson.gmCurrentPatient() 344 if not pat.connected: 345 gmDispatcher.send(signal = 'statustext', msg =_('Cannot change notebook tabs. No active patient.')) 346 event.Veto() 347 return 348 # that test passed, so let's hope things are fine 349 event.Allow() # redundant ? 350 event.Skip() 351 return 352 353 # check target page 354 target_page = self.__gb['horstspace.notebook.pages'][__id_evt_page_after_switch] 355 _log.debug('checking event target page for focussability: %s', target_page) 356 if not target_page.can_receive_focus(): 357 _log.warning('veto()ing page change') 358 event.Veto() 359 return 360 361 # everything seems fine so switch 362 _log.debug('event target page seems focussable') 363 self.__target_page_already_checked = True 364 event.Allow() # redundant ? 365 event.Skip() 366 return
367 368 #----------------------------------------------
369 - def _on_notebook_page_changed(self, event):
370 """Called when notebook page changes.""" 371 372 _log.debug('just after switching notebook tabs') 373 374 _log.debug('id: %s', event.Id) 375 _log.debug('event object (= source notebook): %s = %s', event.EventObject.Id, event.EventObject) 376 _log.debug('this notebook (= event receiver): %s = %s', self.nb.Id, self.nb) 377 if event.EventObject.Id != self.nb.Id: 378 _log.error('this event came from another notebook') 379 380 event.Skip() 381 382 id_nb_page_after_switch = self.nb.GetSelection() 383 id_evt_page_before_switch = event.GetOldSelection() 384 id_evt_page_after_switch = event.GetSelection() 385 386 _log.debug('source/target page state in EVT_NOTEBOOK_PAGE_CHANGED:') 387 _log.debug(' #1 - current notebook page: %s (notebook.GetSelection())', id_nb_page_after_switch) 388 _log.debug(' #2 - event source page: %s (= page event says it is coming from, event.GetOldSelection())', id_evt_page_before_switch) 389 _log.debug(' #3 - event target page: %s (= page event wants to go to, event.GetSelection())', id_evt_page_after_switch) 390 391 if self.__id_nb_page_before_switch != id_evt_page_before_switch: 392 _log.warning('those two really *should* match:') 393 _log.warning(' wx.Notebook.GetSelection(): %s (notebook current page before switch) ', self.__id_nb_page_before_switch) 394 _log.warning(' EVT_NOTEBOOK_PAGE_CHANGED.GetOldSelection(): %s (event source page)' % id_evt_page_before_switch) 395 396 target_page = self.__gb['horstspace.notebook.pages'][id_evt_page_after_switch] 397 398 # well-behaving wxPython port ? 399 if self.__target_page_already_checked: 400 _log.debug('target page (evt=%s, nb=%s) claims to have been checked for focussability already: %s', id_evt_page_after_switch, id_nb_page_after_switch, target_page) 401 target_page.receive_focus() 402 self.__target_page_already_checked = False 403 return 404 405 # no, complain 406 _log.debug('target page not checked for focussability yet: %s', target_page) 407 _log.debug('EVT_NOTEBOOK_PAGE_CHANGED.GetOldSelection(): %s' % id_evt_page_before_switch) 408 _log.debug('EVT_NOTEBOOK_PAGE_CHANGED.GetSelection() : %s' % id_evt_page_after_switch) 409 _log.debug('wx.Notebook.GetSelection() (after switch) : %s' % id_nb_page_after_switch) 410 411 # check the new page just for good measure 412 if target_page.can_receive_focus(): 413 _log.debug('we are lucky: target page *can* receive focus anyway') 414 target_page.receive_focus() 415 return 416 417 _log.error('target page cannot receive focus but too late for veto') 418 return
419 420 # #---------------------------------------------- 421 # def _on_right_click(self, evt): 422 # evt.Skip() 423 # return 424 # 425 # load_menu = wx.Menu() 426 # any_loadable = 0 427 # plugins2load_by_name = gmPlugin.GetPluginLoadList('gui') 428 # plugin = None 429 # for plugin_name in plugins2load_by_name: 430 # try: 431 # plugin = gmPlugin.instantiate_plugin('gui', plugin_name) 432 # except Exception: 433 # continue 434 # # not a plugin 435 # if not isinstance(plugin, gmPlugin.cNotebookPlugin): 436 # plugin = None 437 # continue 438 # # already loaded ? 439 # if plugin.__class__.__name__ in self.guibroker['horstspace.notebook.gui'].keys(): 440 # plugin = None 441 # continue 442 # # add to load menu 443 # item = load_menu.AppendItem(wx.MenuItem(load_menu, plugin.name())) 444 # self.Bind(wx.EVT_MENU, plugin.on_load, load_menu_OR_item) 445 # any_loadable = 1 446 # # make menus 447 # menu = wx.Menu() 448 # ID_LOAD = wx.NewId() 449 # ID_DROP = wx.NewId() 450 # if any_loadable: 451 # menu.Append(ID_LOAD, _('add plugin ...'), load_menu) 452 # plugins = self.guibroker['horstspace.notebook.gui'] 453 # raised_plugin = plugins[self.nb.GetSelection()].name() 454 # item = menu.AppendItem(wx.MenuItem(-1, "drop [%s]" % raised_plugin)) 455 # self.Bind(wx.EVT_MENU, self._on_drop_plugin, item) 456 # self.PopupMenu(menu, evt.GetPosition()) 457 # menu.Destroy() 458 # evt.Skip() 459 460 #----------------------------------------------
461 - def _on_drop_plugin(self, evt):
462 """Unload plugin and drop from load list.""" 463 pages = self.guibroker['horstspace.notebook.pages'] 464 page = pages[self.nb.GetSelection()] 465 page.unregister() 466 self.nb.AdvanceSelection()
467 # FIXME:"dropping" means talking to configurator so not reloaded 468 469 #----------------------------------------------
470 - def _on_hide_plugin (self, evt):
471 """Unload plugin but don't touch configuration.""" 472 # this dictionary links notebook page numbers to plugin objects 473 pages = self.guibroker['horstspace.notebook.pages'] 474 page = pages[self.nb.GetSelection()] 475 page.unregister()
476 477 #============================================================================== 478 if __name__ == '__main__': 479 wx.InitAllImageHandlers() 480 pgbar = gmPluginLoadProgressBar(3) 481