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