Package Gnumed :: Package wxpython :: Module gmTopPanel
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmTopPanel

  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  #=========================================================== 
43 -class cMainTopPanel(wx.Panel):
44
45 - def __init__(self, parent, id):
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 #-------------------------------------------------------
62 - def __do_layout(self):
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 #-------------------------------------------------------
217 - def __register_interests(self):
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 #----------------------------------------------
253 - def _on_allergies_dclicked(self, evt):
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 #----------------------------------------------
274 - def _on_tag_change(self):
275 wx.CallAfter(self.__update_tags)
276 #----------------------------------------------
277 - def _on_name_identity_change(self):
278 wx.CallAfter(self.__on_name_identity_change)
279 #----------------------------------------------
281 self.__update_age_label() 282 self.Layout()
283 #----------------------------------------------
284 - def _on_post_patient_selection(self, **kwargs):
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 #----------------------------------------------
289 - def __on_post_patient_selection(self, **kwargs):
290 self.__update_age_label() 291 self.__update_allergies() 292 self.__update_tags() 293 self.Layout()
294 #-------------------------------------------------------
295 - def __on_display_demographics(self, evt):
296 print "display patient demographic window now"
297 #-------------------------------------------------------
298 - def _update_allergies(self, **kwargs):
299 wx.CallAfter(self.__update_allergies)
300 #------------------------------------------------------- 301 # internal API 302 #-------------------------------------------------------
303 - def __update_tags(self):
304 self._PNL_tags.refresh(patient = self.curr_pat)
305 #-------------------------------------------------------
306 - def __update_age_label(self):
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 #-------------------------------------------------------
341 - def __update_allergies(self, **kwargs):
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 #-------------------------------------------------------
390 - def AddWidgetRightBottom (self, widget):
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 #-------------------------------------------------------
395 - def AddWidgetLeftBottom (self, widget):
396 """Insert a widget on the left-hand side of the bottom toolbar. 397 """ 398 self.szr_bottom_row.Prepend(widget, 0, wx.ALL, 0)
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