| Home | Trees | Indices | Help |
|
|---|
|
|
1 # GNUmed
2
3 #===========================================================
4 __version__ = "$Revision: 1.106 $"
5 __author__ = "R.Terry <rterry@gnumed.net>, I.Haywood <i.haywood@ugrad.unimelb.edu.au>, K.Hilbert <Karsten.Hilbert@gmx.net>"
6 __license__ = "GPL"
7
8
9 import sys, os.path, datetime as pyDT, logging
10
11
12 import wx
13
14
15 from Gnumed.pycommon import gmGuiBroker, gmPG2, gmDispatcher, gmTools, gmCfg2, gmDateTime, gmI18N
16 from Gnumed.business import gmPerson, gmEMRStructItems, gmAllergy
17
18 from Gnumed.wxpython import gmGuiHelpers
19 from Gnumed.wxpython import gmDemographicsWidgets
20 from Gnumed.wxpython import gmAllergyWidgets
21 from Gnumed.wxpython import gmPatSearchWidgets
22 from Gnumed.wxpython import gmEMRStructWidgets
23 from Gnumed.wxpython import gmPatPicWidgets
24
25
26 _log = logging.getLogger('gm.ui')
27 _log.info(__version__)
28
29 [ ID_BTN_pat_demographics,
30 # ID_CBOX_consult_type,
31 ID_BMITOOL,
32 ID_BMIMENU,
33 ID_PREGTOOL,
34 ID_PREGMENU,
35 ID_LOCKBUTTON,
36 ID_LOCKMENU,
37 ] = map(lambda _init_ctrls: wx.NewId(), range(7))
38
39 # FIXME: need a better name here !
40 bg_col = wx.Colour(214,214,214)
41 fg_col = wx.Colour(0,0,131)
42 col_brightred = wx.Colour(255,0,0)
43 #===========================================================
45
47
48 wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, wx.RAISED_BORDER)
49
50 self.__gb = gmGuiBroker.GuiBroker()
51
52 self.__do_layout()
53 self.__register_interests()
54
55 # init plugin toolbars dict
56 #self.subbars = {}
57 self.curr_pat = gmPerson.gmCurrentPatient()
58
59 # and actually display ourselves
60 self.SetAutoLayout(True)
61 self.Show(True)
62 #-------------------------------------------------------
64 """Create the layout.
65
66 .--------------------------------.
67 | patient | top row |
68 | picture |----------------------|
69 | | bottom row |
70 `--------------------------------'
71 """
72 self.SetBackgroundColour(bg_col)
73
74 # create rows
75 # - top row
76 # .----------------------------.
77 # | patient | age | allergies |
78 # | selector | | |
79 # `----------------------------'
80 self.szr_top_row = wx.BoxSizer(wx.HORIZONTAL)
81
82 # - details button
83 # fname = os.path.join(self.__gb['gnumed_dir'], 'bitmaps', 'binoculars_form.png')
84 # img = wxImage(fname, wx.BITMAP_TYPE_ANY)
85 # bmp = wx.BitmapFromImage(img)
86 # self.btn_pat_demographics = wx.BitmapButton (
87 # parent = self,
88 # id = ID_BTN_pat_demographics,
89 # bitmap = bmp,
90 # style = wx.BU_EXACTFIT | wxNO_BORDER
91 # )
92 # self.btn_pat_demographics.SetToolTip(wxToolTip(_("display patient demographics")))
93 # self.szr_top_row.Add (self.btn_pat_demographics, 0, wxEXPAND | wx.BOTTOM, 3)
94
95 # padlock button - Dare I say HIPAA ?
96 # fname = os.path.join(self.__gb['gnumed_dir'], 'bitmaps', 'padlock_closed.png')
97 # img = wxImage(fname, wx.BITMAP_TYPE_ANY)
98 # bmp = wx.BitmapFromImage(img)
99 # self.btn_lock = wx.BitmapButton (
100 # parent = self,
101 # id = ID_LOCKBUTTON,
102 # bitmap = bmp,
103 # style = wx.BU_EXACTFIT | wxNO_BORDER
104 # )
105 # self.btn_lock.SetToolTip(wxToolTip(_('lock client')))
106 # self.szr_top_row.Add(self.btn_lock, 0, wxALL, 3)
107
108 # - patient selector
109 self.patient_selector = gmPatSearchWidgets.cActivePatientSelector(self, -1)
110 cfg = gmCfg2.gmCfgData()
111 if cfg.get(option = 'slave'):
112 self.patient_selector.SetEditable(0)
113 self.patient_selector.SetToolTip(None)
114 self.patient_selector.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD, False, ''))
115
116 # - age
117 self.lbl_age = wx.StaticText(self, -1, u'', style = wx.ALIGN_CENTER_VERTICAL)
118 self.lbl_age.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD, False, ''))
119
120 # - allergies (substances only, like "makrolides, penicillins, eggs")
121 self.lbl_allergies = wx.StaticText (self, -1, _('Caveat'), style = wx.ALIGN_CENTER_VERTICAL)
122 self.lbl_allergies.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD, False, ''))
123 self.lbl_allergies.SetBackgroundColour(bg_col)
124 self.lbl_allergies.SetForegroundColour(col_brightred)
125 self.txt_allergies = wx.TextCtrl (self, -1, "", style = wx.TE_READONLY)
126 self.txt_allergies.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD, False, ''))
127 self.txt_allergies.SetForegroundColour (col_brightred)
128
129 self.szr_top_row.Add(self.patient_selector, 6, wx.LEFT | wx.BOTTOM, 3)
130 self.szr_top_row.Add(self.lbl_age, 0, wx.ALL, 3)
131 self.szr_top_row.Add(self.lbl_allergies, 0, wx.ALL, 3)
132 self.szr_top_row.Add(self.txt_allergies, 8, wx.BOTTOM, 3)
133
134 # - bottom row
135 # .----------------------------------------------------------.
136 # | plugin toolbar | bmi | edc | | encounter | lock |
137 # | | | | | type sel | |
138 # `----------------------------------------------------------'
139 #self.tb_lock.AddControl(wx.StaticBitmap(self.tb_lock, -1, getvertical_separator_thinBitmap(), wx.DefaultPosition, wx.DefaultSize))
140
141 # (holds most of the buttons)
142 self.szr_bottom_row = wx.BoxSizer(wx.HORIZONTAL)
143 self._PNL_tags = gmDemographicsWidgets.cImageTagPresenterPnl(self, -1)
144 self.szr_bottom_row.Add(self._PNL_tags, 2, wx.ALIGN_CENTER_VERTICAL, 0)
145
146 # spacer
147 self.szr_bottom_row.Add((20, 20), 1, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 0)
148
149 pnl_enc = gmEMRStructWidgets.cActiveEncounterPnl(self, -1)
150 self.szr_bottom_row.Add(pnl_enc, 1, wx.ALIGN_CENTER_VERTICAL, 0)
151
152 # self.pnl_bottom_row = wx.Panel(self, -1)
153 # self.szr_bottom_row.Add(self.pnl_bottom_row, 6, wx.GROW, 0)
154
155 # BMI calculator button
156 # fname = os.path.join(self.__gb['gnumed_dir'], 'bitmaps', 'bmi_calculator.png')
157 # img = wx.Image(fname, wx.BITMAP_TYPE_ANY)
158 # bmp = wx.BitmapFromImage(img)
159 # self.btn_bmi = wx.BitmapButton (
160 # parent = self,
161 # id = ID_BMITOOL,
162 # bitmap = bmp,
163 # style = wx.BU_EXACTFIT | wx.NO_BORDER
164 # )
165 # self.btn_bmi.SetToolTip(wx.ToolTip(_("BMI Calculator")))
166 # self.szr_bottom_row.Add(self.btn_bmi, 0)
167
168 # tb = wxToolBar(self, -1, style=wx.TB_HORIZONTAL | wxNO_BORDER | wx.TB_FLAT)
169 # tb.AddTool (
170 # ID_BMITOOL,
171 # gmImgTools.xpm2bmp(bmicalculator.get_xpm()),
172 # shortHelpString = _("BMI Calculator")
173 # )
174 # self.szr_bottom_row.Add(tb, 0, wxRIGHT, 0)
175
176 # pregnancy calculator button
177 # fname = os.path.join(self.__gb['gnumed_dir'], 'bitmaps', 'preg_calculator.png')
178 # img = wxImage(fname, wx.BITMAP_TYPE_ANY)
179 # bmp = wx.BitmapFromImage(img)
180 # self.btn_preg = wx.BitmapButton (
181 # parent = self,
182 # id = ID_PREGTOOL,
183 # bitmap = bmp,
184 # style = wx.BU_EXACTFIT | wxNO_BORDER
185 # )
186 # self.btn_preg.SetToolTip(wxToolTip(_("Pregnancy Calculator")))
187 # self.szr_bottom_row.Add(self.btn_preg, 0)
188
189 # - stack them atop each other
190 self.szr_stacked_rows = wx.BoxSizer(wx.VERTICAL)
191 # ??? (IMHO: space is at too much of a premium for such padding)
192 # FIXME: deuglify
193 try:
194 self.szr_stacked_rows.Add(1, 1, 0)
195 except:
196 self.szr_stacked_rows.Add((1, 1), 0)
197
198 # 0 here indicates the sizer cannot change its heights - which is intended
199 self.szr_stacked_rows.Add(self.szr_top_row, 0, wx.EXPAND)
200 self.szr_stacked_rows.Add(self.szr_bottom_row, 1, wx.EXPAND|wx.TOP, 5)
201
202 # create patient picture
203 self.patient_picture = gmPatPicWidgets.cPatientPicture(self, -1)
204 # tt = wx.ToolTip(_('Patient picture.\nRight-click for context menu.'))
205 # self.patient_picture.SetToolTip(tt)
206
207 # create main sizer
208 self.szr_main = wx.BoxSizer(wx.HORIZONTAL)
209 # - insert patient picture
210 self.szr_main.Add(self.patient_picture, 0, wx.LEFT | wx.TOP | wx.Right, 5)
211 # - insert stacked rows
212 self.szr_main.Add(self.szr_stacked_rows, 1)
213
214 # associate ourselves with our main sizer
215 self.SetSizer(self.szr_main)
216 # and auto-size to minimum calculated size
217 self.szr_main.Fit(self)
218 #-------------------------------------------------------
219 # internal helpers
220 #-------------------------------------------------------
221 #-------------------------------------------------------
222 # event handling
223 #-------------------------------------------------------
225 # events
226 wx.EVT_BUTTON(self, ID_BTN_pat_demographics, self.__on_display_demographics)
227
228 # tools_menu = self.__gb['main.toolsmenu']
229
230 # - BMI calculator
231 # wx.EVT_BUTTON(self, ID_BMITOOL, self._on_show_BMI)
232 # tools_menu.Append(ID_BMIMENU, _("BMI"), _("Body Mass Index Calculator"))
233 # wx.EVT_MENU(main_frame, ID_BMIMENU, self._on_show_BMI)
234
235 # - pregnancy calculator
236 # wx.EVT_BUTTON(self, ID_PREGTOOL, self._on_show_Preg_Calc)
237 # tools_menu.Append(ID_PREGMENU, _("EDC"), _("Pregnancy Calculator"))
238 # wx.EVT_MENU(main_frame, ID_PREGMENU, self._on_show_Preg_Calc)
239
240 # - lock button
241 # wx.EVT_BUTTON(self, ID_LOCKBUTTON, self._on_lock)
242 # tools_menu.Append(ID_LOCKMENU, _("lock client"), _("locks client and hides data"))
243 # wx.EVT_MENU(main_frame, ID_LOCKMENU, self._on_lock)
244
245 wx.EVT_LEFT_DCLICK(self.txt_allergies, self._on_allergies_dclicked)
246
247 # client internal signals
248 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
249 gmDispatcher.connect(signal = u'allg_mod_db', receiver = self._update_allergies)
250 gmDispatcher.connect(signal = u'allg_state_mod_db', receiver = self._update_allergies)
251 gmDispatcher.connect(signal = u'name_mod_db', receiver = self._on_name_identity_change)
252 gmDispatcher.connect(signal = u'identity_mod_db', receiver = self._on_name_identity_change)
253 gmDispatcher.connect(signal = u'identity_tag_mod_db', receiver = self._on_tag_change)
254 #----------------------------------------------
255 # def _on_lock(self, evt):
256 # print "should be locking client now by obscuring data"
257 # print "and popping up a modal dialog box asking for a"
258 # print "password to reactivate"
259 #----------------------------------------------
261 pat = gmPerson.gmCurrentPatient()
262 if not pat.connected:
263 gmDispatcher.send('statustext', msg = _('Cannot activate Allergy Manager. No active patient.'))
264 return
265 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent=self, id=-1)
266 dlg.ShowModal()
267 return
268 #----------------------------------------------
269 # def _on_show_BMI(self, evt):
270 # FIXME: update patient ID ?
271 # bmi = gmBMIWidgets.BMI_Frame(self)
272 # bmi.Centre(wx.BOTH)
273 # bmi.Show(1)
274 #----------------------------------------------
275 # def _on_show_Preg_Calc(self, evt):
276 # FIXME: update patient ID ?
277 # pc = gmPregWidgets.cPregCalcFrame(self)
278 # pc.Centre(wx.BOTH)
279 # pc.Show(1)
280 #----------------------------------------------
283 #----------------------------------------------
286 #----------------------------------------------
290 #----------------------------------------------
292 # needed because GUI stuff can't be called from a thread (and that's
293 # where we are coming from via backend listener -> dispatcher)
294 wx.CallAfter(self.__on_post_patient_selection, **kwargs)
295 #----------------------------------------------
297 self.__update_age_label()
298 self.__update_allergies()
299 self.__update_tags()
300 self.Layout()
301 #-------------------------------------------------------
304 #-------------------------------------------------------
307 #-------------------------------------------------------
308 # internal API
309 #-------------------------------------------------------
312 #-------------------------------------------------------
314
315 if self.curr_pat['deceased'] is None:
316
317 if self.curr_pat.get_formatted_dob(format = '%m-%d') == pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone).strftime('%m-%d'):
318 template = _('%s %s (%s today !)')
319 else:
320 template = u'%s %s (%s)'
321
322 # FIXME: if the age is below, say, 2 hours we should fire
323 # a timer here that updates the age in increments of 1 minute ... :-)
324 age = template % (
325 gmPerson.map_gender2symbol[self.curr_pat['gender']],
326 self.curr_pat.get_formatted_dob(format = '%d %b %Y', encoding = gmI18N.get_encoding()),
327 self.curr_pat['medical_age']
328 )
329
330 # Easter Egg ;-)
331 if self.curr_pat['lastnames'] == u'Leibner':
332 if self.curr_pat['firstnames'] == u'Steffi':
333 if self.curr_pat['preferred'] == u'Wildfang':
334 age = u'%s %s' % (gmTools.u_black_heart, age)
335
336 else:
337
338 template = u'%s %s - %s (%s)'
339 age = template % (
340 gmPerson.map_gender2symbol[self.curr_pat['gender']],
341 self.curr_pat.get_formatted_dob(format = '%d.%b %Y', encoding = gmI18N.get_encoding()),
342 self.curr_pat['deceased'].strftime('%d.%b %Y').decode(gmI18N.get_encoding()),
343 self.curr_pat['medical_age']
344 )
345
346 self.lbl_age.SetLabel(age)
347 #-------------------------------------------------------
349
350 emr = self.curr_pat.get_emr()
351 state = emr.allergy_state
352
353 # state in tooltip
354 if state['last_confirmed'] is None:
355 confirmed = _('never')
356 else:
357 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
358 tt = (state.state_string + (90 * u' '))[:90] + u'\n'
359 tt += _('last confirmed %s\n') % confirmed
360 tt += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s') % state['modified_by'])
361 tt += u'\n'
362
363 # allergies
364 tmp = []
365 for allergy in emr.get_allergies():
366 # in field: "true" allergies only, not intolerances
367 if allergy['type'] == 'allergy':
368 tmp.append(allergy['descriptor'][:10].strip() + gmTools.u_ellipsis)
369 # in tooltip
370 if allergy['definite']:
371 certainty = _('definite')
372 else:
373 certainty = _('suspected')
374 reaction = gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
375 if len(reaction) > 50:
376 reaction = reaction[:50] + gmTools.u_ellipsis
377 tt += u'%s (%s, %s): %s\n' % (
378 allergy['descriptor'],
379 allergy['l10n_type'],
380 certainty,
381 reaction
382 )
383
384 if len(tmp) == 0:
385 tmp = state.state_symbol
386 else:
387 tmp = ','.join(tmp)
388
389 if state['last_confirmed'] is not None:
390 tmp += state['last_confirmed'].strftime(' (%x)')
391
392 self.txt_allergies.SetValue(tmp)
393 self.txt_allergies.SetToolTipString(tt)
394 #-------------------------------------------------------
395 # remote layout handling
396 #-------------------------------------------------------
398 """Insert a widget on the right-hand side of the bottom toolbar.
399 """
400 self.szr_bottom_row.Add(widget, 0, wx.RIGHT, 0)
401 #-------------------------------------------------------
406 #-------------------------------------------------------
407 # def CreateBar(self):
408 # """Creates empty toolbar suited for adding to top panel."""
409 # bar = wx.ToolBar (
410 # self.pnl_bottom_row,
411 # -1,
412 # size = self.pnl_bottom_row.GetClientSize(),
413 # style = wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT
414 # )
415 # return bar
416 #-------------------------------------------------------
417 # def AddBar(self, key=None, bar=None):
418 # """Creates and returns a new empty toolbar, referenced by key.
419 #
420 # Key should correspond to the notebook page number as defined
421 # by the notebook (see gmPlugin.py), so that gmGuiMain can
422 # display the toolbar with the notebook
423 # """
424 # bar.SetToolBitmapSize((16,16))
425 # self.subbars[key] = bar
426 # if len(self.subbars) == 1:
427 # bar.Show(1)
428 # self.__current = key
429 # else:
430 # bar.Hide()
431 # return True
432 #-------------------------------------------------------
433 # def ReFit (self):
434 # """Refits the toolbar after its been changed
435 # """
436 # tw = 0
437 # th = 0
438 # # get maximum size for the toolbar
439 # for i in self.subbars.values ():
440 # ntw, nth = i.GetSizeTuple ()
441 # if ntw > tw:
442 # tw = ntw
443 # if nth > th:
444 # th = nth
445 # #import pdb
446 # #pdb.set_trace ()
447 # sz = wx.Size (tw, th)
448 # self.pnl_bottom_row.SetSize(sz)
449 # for i in self.subbars.values():
450 # i.SetSize (sz)
451 # self.szr_main.Layout()
452 # self.szr_main.Fit(self)
453 #-------------------------------------------------------
454 # def ShowBar (self, key):
455 # """Displays the named toolbar.
456 # """
457 # self.subbars[self.__current].Hide()
458 # try:
459 # self.subbars[key].Show(1)
460 # self.__current = key
461 # except KeyError:
462 # _log.exception("cannot show undefined toolbar [%s]" % key)
463 #-------------------------------------------------------
464 # def DeleteBar (self, key):
465 # """Removes a toolbar.
466 # """
467 # try:
468 # self.subbars[key].Destroy()
469 # del self.subbars[key]
470 # # FIXME: ??
471 # if self.__current == key and len(self.subbars):
472 # self.__current = self.subbars.keys()[0]
473 # self.subbars[self.__current].Show(1)
474 # except KeyError:
475 # _log.exception("cannot delete undefined toolbar [%s]" % key)
476
477 #===========================================================
478 if __name__ == "__main__":
479 wx.InitAllImageHandlers()
480 app = wxPyWidgetTester(size = (400, 200))
481 app.SetWidget(cMainTopPanel, -1)
482 app.MainLoop()
483 #===========================================================
484
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Dec 5 04:00:03 2011 | http://epydoc.sourceforge.net |