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