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
29
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
39
40
41
42
44
45
46
47
48
49
50 self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self._on_notebook_page_changing)
51
52
53
54 self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self._on_notebook_page_changed)
55
56
57
58
59
60
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
86 if __id_evt_page_after_switch == self.__id_evt_page_before_switch:
87
88
89
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
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
104 event.Allow()
105 event.Skip()
106 return
107
108
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
117 _log.debug('event target page seems focussable')
118 self.__target_page_already_checked = True
119 event.Allow()
120 event.Skip()
121 return
122
123
125 """Called when notebook page changes.
126
127 Date: Wed, 15 May 2019 09:03:49 -0700 (PDT)
128 From: Robin Dunn <robin@alldunn.com>
129 To: wxPython-users <wxpython-users@googlegroups.com>
130 Subject: [wxPython-users] Re: wx.Notebook events work differently on Mac vs Linux and Windows
131
132 While in the _CHANGED event the notebook's current page (what you get from
133 GetCurrentPage) still hasn't been updated. I agree that this seems
134 unintuitive but it's always been that way. There may be a good reason for
135 it, but I don't know what it is.
136
137 Instead, you can use the event's GetSelection to get the index of the page
138 being changed to, and then use the notebook's GetPage to get the page object.
139
140 Also, you should call event.Skip() in your _CHANGED handler. I think there
141 is some layout things happening in the default handler on one or more of
142 the platforms, so you want to ensure that can still happen.
143 --
144 Robin
145 """
146 _log.debug('just after switching notebook tabs')
147
148 event.Skip()
149
150 _log.debug('id: %s', event.Id)
151 _log.debug('event object (= source notebook): %s = %s', event.EventObject.Id, event.EventObject)
152 _log.debug('this notebook (= event receiver): %s = %s', self.Id, self)
153 if event.EventObject.Id != self.Id:
154 _log.error('this event came from another notebook')
155
156 id_nb_page_after_switch = self.GetSelection()
157 id_evt_page_before_switch = event.GetOldSelection()
158 id_evt_page_after_switch = event.GetSelection()
159
160 _log.debug('source/target page state in EVT_NOTEBOOK_PAGE_CHANGED:')
161 _log.debug(' #1 - current notebook page: %s (notebook.GetSelection())', id_nb_page_after_switch)
162 _log.debug(' #2 - event source page: %s (= page event says it is coming from, event.GetOldSelection())', id_evt_page_before_switch)
163 _log.debug(' #3 - event target page: %s (= page event wants to go to, event.GetSelection())', id_evt_page_after_switch)
164
165 if self.__id_nb_page_before_switch != id_evt_page_before_switch:
166 _log.warning('those two really *should* match:')
167 _log.warning(' wx.Notebook.GetSelection(): %s (notebook current page before switch) ', self.__id_nb_page_before_switch)
168 _log.warning(' EVT_NOTEBOOK_PAGE_CHANGED.GetOldSelection(): %s (event source page)' % id_evt_page_before_switch)
169
170 target_page = self.__gb['horstspace.notebook.pages'][id_evt_page_after_switch]
171
172
173 if self.__target_page_already_checked:
174 _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)
175 target_page.receive_focus()
176 self.__target_page_already_checked = False
177 return
178
179
180 _log.debug('target page not checked for focussability yet: %s', target_page)
181 _log.debug('EVT_NOTEBOOK_PAGE_CHANGED.GetOldSelection(): %s' % id_evt_page_before_switch)
182 _log.debug('EVT_NOTEBOOK_PAGE_CHANGED.GetSelection() : %s' % id_evt_page_after_switch)
183 _log.debug('wx.Notebook.GetSelection() (after switch) : %s' % id_nb_page_after_switch)
184
185
186 if target_page.can_receive_focus():
187 _log.debug('we are lucky: target page *can* receive focus anyway')
188 target_page.receive_focus()
189 return
190
191 _log.error('target page cannot receive focus but too late for veto')
192 return
193
194
195
197 """GNUmed inner-frame layout manager.
198
199 This implements a Horst-space notebook-only
200 "inner-frame" layout manager.
201 """
203
204 wx.Panel.__init__(
205 self,
206 parent = parent,
207 id = id,
208 pos = wx.DefaultPosition,
209 size = wx.DefaultSize,
210 style = wx.NO_BORDER,
211 name = 'HorstSpace.LayoutMgrPnl'
212 )
213
214
215 self.top_panel = gmTopPanel.cTopPnl(self, -1)
216
217
218 self.nb = cHorstSpaceNotebook(parent = self)
219
220
221 self.__gb = gmGuiBroker.GuiBroker()
222 self.__gb['horstspace.top_panel'] = self.top_panel
223 self.__gb['horstspace.notebook'] = self.nb
224 self.__load_plugins()
225
226
227 self.main_szr = wx.BoxSizer(wx.VERTICAL)
228 self.main_szr.Add(self.top_panel, 0, wx.EXPAND)
229 self.main_szr.Add(self.nb, 1, wx.EXPAND)
230 self.SetSizer(self.main_szr)
231
232
233
234
235 self.__register_events()
236
237
238
239
241
242
243
244
245
246 self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self._on_notebook_page_changing, self.nb)
247
248
249 self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self._on_notebook_page_changed, self.nb)
250
251
252
253 gmDispatcher.connect(self._on_post_patient_selection, 'post_patient_selection')
254
255
257
258 wx.BeginBusyCursor()
259
260
261 plugins2load_by_name = gmPlugin.GetPluginLoadList (
262 option = 'horstspace.notebook.plugin_load_order',
263 plugin_dir = 'gui',
264 defaults = ['gmProviderInboxPlugin']
265 )
266
267 _log.debug('plugin load order: %s', plugins2load_by_name)
268
269 nr_plugins = len(plugins2load_by_name)
270 failed_plugins = []
271
272
273 progress_bar = gmPlugin.cLoadProgressBar(nr_plugins)
274
275
276 prev_plugin = ""
277 first_plugin = None
278 plugin = None
279 result = -1
280 for idx in range(nr_plugins):
281 curr_plugin = plugins2load_by_name[idx]
282 progress_bar.Update(result, curr_plugin)
283 try:
284 plugin = gmPlugin.instantiate_plugin('gui', curr_plugin)
285 if plugin:
286 plugin.register()
287 result = 1
288 else:
289 _log.error("plugin [%s] not loaded, see errors above", curr_plugin)
290 failed_plugins.append(curr_plugin)
291 result = 1
292 except:
293 _log.exception('failed to load plugin %s', curr_plugin)
294 failed_plugins.append(curr_plugin)
295 result = 0
296
297 if first_plugin is None:
298 first_plugin = plugin
299 prev_plugin = curr_plugin
300
301 _log.debug('failed plugins: %s', failed_plugins)
302 progress_bar.DestroyLater()
303 wx.EndBusyCursor()
304
305
306 page = self.nb.GetPage(0)
307 page.Refresh()
308
309 return True
310
311
312
313
315 db_cfg = gmCfg.cCfgSQL()
316 default_plugin = db_cfg.get2 (
317 option = 'patient_search.plugin_to_raise_after_search',
318 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
319 bias = 'user',
320 default = 'gmPatientOverviewPlugin'
321 )
322 gmDispatcher.send(signal = 'display_widget', name = default_plugin)
323
324
326 """Called before notebook page change is processed."""
327
328 _log.debug('just before switching notebook tabs')
329
330 _log.debug('id: %s', event.Id)
331 _log.debug('event object (= source notebook): %s = %s', event.EventObject.Id, event.EventObject)
332 _log.debug('this notebook (= event receiver): %s = %s', self.nb.Id, self.nb)
333 if event.EventObject.Id != self.nb.Id:
334 _log.error('this event came from another notebook')
335
336 self.__target_page_already_checked = False
337
338 self.__id_nb_page_before_switch = self.nb.GetSelection()
339 self.__id_evt_page_before_switch = event.GetOldSelection()
340 __id_evt_page_after_switch = event.GetSelection()
341
342 _log.debug('source/target page state in EVT_NOTEBOOK_PAGE_CHANGING:')
343 _log.debug(' #1 - notebook current page: %s (= notebook.GetSelection())', self.__id_nb_page_before_switch)
344 _log.debug(' #2 - event source page: %s (= page event says it is coming from, event.GetOldSelection())', self.__id_evt_page_before_switch)
345 _log.debug(' #3 - event target page: %s (= page event wants to go to, event.GetSelection())', __id_evt_page_after_switch)
346 if self.__id_evt_page_before_switch != self.__id_nb_page_before_switch:
347 _log.warning(' problem: #1 and #2 really should match but do not')
348
349
350 if __id_evt_page_after_switch == self.__id_evt_page_before_switch:
351
352
353
354 _log.debug('this system is: sys: [%s] wx: [%s]', sys.platform, wx.Platform)
355 _log.debug('it seems to be one of those platforms that have no clue which notebook page they are switching to')
356 _log.debug('(Windows is documented to return the old page from both evt.GetOldSelection() and evt.GetSelection())')
357 _log.debug('current notebook page : %s', self.__id_nb_page_before_switch)
358 _log.debug('source page from event: %s', self.__id_evt_page_before_switch)
359 _log.debug('target page from event: %s', __id_evt_page_after_switch)
360 _log.warning('cannot check whether notebook page change needs to be vetoed')
361
362 pat = gmPerson.gmCurrentPatient()
363 if not pat.connected:
364 gmDispatcher.send(signal = 'statustext', msg =_('Cannot change notebook tabs. No active patient.'))
365 event.Veto()
366 return
367
368 event.Allow()
369 event.Skip()
370 return
371
372
373 target_page = self.__gb['horstspace.notebook.pages'][__id_evt_page_after_switch]
374 _log.debug('checking event target page for focussability: %s', target_page)
375 if not target_page.can_receive_focus():
376 _log.warning('veto()ing page change')
377 event.Veto()
378 return
379
380
381 _log.debug('event target page seems focussable')
382 self.__target_page_already_checked = True
383 event.Allow()
384 event.Skip()
385 return
386
387
389 """Called when notebook page changes."""
390
391 _log.debug('just after switching notebook tabs')
392
393 _log.debug('id: %s', event.Id)
394 _log.debug('event object (= source notebook): %s = %s', event.EventObject.Id, event.EventObject)
395 _log.debug('this notebook (= event receiver): %s = %s', self.nb.Id, self.nb)
396 if event.EventObject.Id != self.nb.Id:
397 _log.error('this event came from another notebook')
398
399 event.Skip()
400
401 id_nb_page_after_switch = self.nb.GetSelection()
402 id_evt_page_before_switch = event.GetOldSelection()
403 id_evt_page_after_switch = event.GetSelection()
404
405 _log.debug('source/target page state in EVT_NOTEBOOK_PAGE_CHANGED:')
406 _log.debug(' #1 - current notebook page: %s (notebook.GetSelection())', id_nb_page_after_switch)
407 _log.debug(' #2 - event source page: %s (= page event says it is coming from, event.GetOldSelection())', id_evt_page_before_switch)
408 _log.debug(' #3 - event target page: %s (= page event wants to go to, event.GetSelection())', id_evt_page_after_switch)
409
410 if self.__id_nb_page_before_switch != id_evt_page_before_switch:
411 _log.warning('those two really *should* match:')
412 _log.warning(' wx.Notebook.GetSelection(): %s (notebook current page before switch) ', self.__id_nb_page_before_switch)
413 _log.warning(' EVT_NOTEBOOK_PAGE_CHANGED.GetOldSelection(): %s (event source page)' % id_evt_page_before_switch)
414
415 target_page = self.__gb['horstspace.notebook.pages'][id_evt_page_after_switch]
416
417
418 if self.__target_page_already_checked:
419 _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)
420 target_page.receive_focus()
421 self.__target_page_already_checked = False
422 return
423
424
425 _log.debug('target page not checked for focussability yet: %s', target_page)
426 _log.debug('EVT_NOTEBOOK_PAGE_CHANGED.GetOldSelection(): %s' % id_evt_page_before_switch)
427 _log.debug('EVT_NOTEBOOK_PAGE_CHANGED.GetSelection() : %s' % id_evt_page_after_switch)
428 _log.debug('wx.Notebook.GetSelection() (after switch) : %s' % id_nb_page_after_switch)
429
430
431 if target_page.can_receive_focus():
432 _log.debug('we are lucky: target page *can* receive focus anyway')
433 target_page.receive_focus()
434 return
435
436 _log.error('target page cannot receive focus but too late for veto')
437 return
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
481 """Unload plugin and drop from load list."""
482 pages = self.guibroker['horstspace.notebook.pages']
483 page = pages[self.nb.GetSelection()]
484 page.unregister()
485 self.nb.AdvanceSelection()
486
487
488
490 """Unload plugin but don't touch configuration."""
491
492 pages = self.guibroker['horstspace.notebook.pages']
493 page = pages[self.nb.GetSelection()]
494 page.unregister()
495
496
497 if __name__ == '__main__':
498 wx.InitAllImageHandlers()
499 pgbar = gmPluginLoadProgressBar(3)
500