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

Source Code for Module Gnumed.wxpython.gmGuiHelpers

  1  """GNUmed GUI helper classes and functions. 
  2   
  3  This module provides some convenient wxPython GUI 
  4  helper thingies that are widely used throughout 
  5  GNUmed. 
  6  """ 
  7  # ======================================================================== 
  8  __author__  = "K. Hilbert <Karsten.Hilbert@gmx.net>" 
  9  __license__ = "GPL v2 or later (details at http://www.gnu.org)" 
 10   
 11  import os 
 12  import logging 
 13  import sys 
 14   
 15   
 16  import wx 
 17   
 18   
 19  if __name__ == '__main__': 
 20          sys.path.insert(0, '../../') 
 21  from Gnumed.pycommon import gmMatchProvider 
 22  from Gnumed.pycommon import gmExceptions 
 23  from Gnumed.pycommon import gmLog2 
 24  from Gnumed.pycommon import gmTools 
 25  from Gnumed.wxpython import gmPhraseWheel 
 26   
 27   
 28  _log = logging.getLogger('gm.main') 
 29  # ======================================================================== 
30 -class cThreeValuedLogicPhraseWheel(gmPhraseWheel.cPhraseWheel):
31
32 - def __init__(self, *args, **kwargs):
33 34 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 35 36 items = [ 37 {'list_label': _('Yes: + / ! / 1'), 'field_label': _('yes'), 'data': True, 'weight': 0}, 38 {'list_label': _('No: - / 0'), 'field_label': _('no'), 'data': False, 'weight': 1}, 39 {'list_label': _('Unknown: ?'), 'field_label': _('unknown'), 'data': None, 'weight': 2}, 40 ] 41 mp = gmMatchProvider.cMatchProvider_FixedList(items) 42 mp.setThresholds(1, 1, 2) 43 mp.word_separators = '[ :/]+' 44 mp.word_separators = None 45 mp.ignored_chars = r"[.'\\(){}\[\]<>~#*$%^_=&@\t23456]+" + r'"' 46 47 self.matcher = mp
48 # ======================================================================== 49 from Gnumed.wxGladeWidgets import wxg2ButtonQuestionDlg 50
51 -class c2ButtonQuestionDlg(wxg2ButtonQuestionDlg.wxg2ButtonQuestionDlg):
52
53 - def __init__(self, *args, **kwargs):
54 55 caption = kwargs['caption'] 56 question = kwargs['question'] 57 button_defs = kwargs['button_defs'][:2] 58 del kwargs['caption'] 59 del kwargs['question'] 60 del kwargs['button_defs'] 61 62 try: 63 show_checkbox = kwargs['show_checkbox'] 64 del kwargs['show_checkbox'] 65 except KeyError: 66 show_checkbox = False 67 68 try: 69 checkbox_msg = kwargs['checkbox_msg'] 70 del kwargs['checkbox_msg'] 71 except KeyError: 72 checkbox_msg = None 73 74 try: 75 checkbox_tooltip = kwargs['checkbox_tooltip'] 76 del kwargs['checkbox_tooltip'] 77 except KeyError: 78 checkbox_tooltip = None 79 80 wxg2ButtonQuestionDlg.wxg2ButtonQuestionDlg.__init__(self, *args, **kwargs) 81 82 self.SetTitle(title = caption) 83 self._LBL_question.SetLabel(label = question) 84 85 if not show_checkbox: 86 self._CHBOX_dont_ask_again.Hide() 87 else: 88 if checkbox_msg is not None: 89 self._CHBOX_dont_ask_again.SetLabel(checkbox_msg) 90 if checkbox_tooltip is not None: 91 self._CHBOX_dont_ask_again.SetToolTipString(checkbox_tooltip) 92 93 buttons = [self._BTN_1, self._BTN_2] 94 for idx in range(len(button_defs)): 95 buttons[idx].SetLabel(label = button_defs[idx]['label']) 96 buttons[idx].SetToolTipString(button_defs[idx]['tooltip']) 97 try: 98 if button_defs[idx]['default'] is True: 99 buttons[idx].SetDefault() 100 buttons[idx].SetFocus() 101 except KeyError: 102 pass 103 104 self.Fit()
105 #--------------------------------------------------------
106 - def checkbox_is_checked(self):
107 return self._CHBOX_dont_ask_again.IsChecked()
108 #-------------------------------------------------------- 109 # event handlers 110 #--------------------------------------------------------
111 - def _on_BTN_1_pressed(self, evt):
112 if self.IsModal(): 113 self.EndModal(wx.ID_YES) 114 else: 115 self.Close()
116 #--------------------------------------------------------
117 - def _on_BTN_2_pressed(self, evt):
118 if self.IsModal(): 119 self.EndModal(wx.ID_NO) 120 else: 121 self.Close()
122 # ======================================================================== 123 from Gnumed.wxGladeWidgets import wxg3ButtonQuestionDlg 124
125 -class c3ButtonQuestionDlg(wxg3ButtonQuestionDlg.wxg3ButtonQuestionDlg):
126
127 - def __init__(self, *args, **kwargs):
128 129 caption = kwargs['caption'] 130 question = kwargs['question'] 131 button_defs = kwargs['button_defs'][:3] 132 del kwargs['caption'] 133 del kwargs['question'] 134 del kwargs['button_defs'] 135 136 try: 137 show_checkbox = kwargs['show_checkbox'] 138 del kwargs['show_checkbox'] 139 except KeyError: 140 show_checkbox = False 141 142 try: 143 checkbox_msg = kwargs['checkbox_msg'] 144 del kwargs['checkbox_msg'] 145 except KeyError: 146 checkbox_msg = None 147 148 try: 149 checkbox_tooltip = kwargs['checkbox_tooltip'] 150 del kwargs['checkbox_tooltip'] 151 except KeyError: 152 checkbox_tooltip = None 153 154 wxg3ButtonQuestionDlg.wxg3ButtonQuestionDlg.__init__(self, *args, **kwargs) 155 156 self.SetTitle(title = caption) 157 self._LBL_question.SetLabel(label = question) 158 159 if not show_checkbox: 160 self._CHBOX_dont_ask_again.Hide() 161 else: 162 if checkbox_msg is not None: 163 self._CHBOX_dont_ask_again.SetLabel(checkbox_msg) 164 if checkbox_tooltip is not None: 165 self._CHBOX_dont_ask_again.SetToolTipString(checkbox_tooltip) 166 167 buttons = [self._BTN_1, self._BTN_2, self._BTN_3] 168 for idx in range(len(button_defs)): 169 buttons[idx].SetLabel(label = button_defs[idx]['label']) 170 buttons[idx].SetToolTipString(button_defs[idx]['tooltip']) 171 try: 172 if button_defs[idx]['default'] is True: 173 buttons[idx].SetDefault() 174 buttons[idx].SetFocus() 175 except KeyError: 176 pass 177 178 self.Fit()
179 #--------------------------------------------------------
180 - def checkbox_is_checked(self):
181 return self._CHBOX_dont_ask_again.IsChecked()
182 #-------------------------------------------------------- 183 # event handlers 184 #--------------------------------------------------------
185 - def _on_BTN_1_pressed(self, evt):
186 if self.IsModal(): 187 self.EndModal(wx.ID_YES) 188 else: 189 self.Close()
190 #--------------------------------------------------------
191 - def _on_BTN_2_pressed(self, evt):
192 if self.IsModal(): 193 self.EndModal(wx.ID_NO) 194 else: 195 self.Close()
196 # ======================================================================== 197 from Gnumed.wxGladeWidgets import wxgMultilineTextEntryDlg 198
199 -class cMultilineTextEntryDlg(wxgMultilineTextEntryDlg.wxgMultilineTextEntryDlg):
200 """Editor for a bit of text.""" 201
202 - def __init__(self, *args, **kwargs):
203 204 try: 205 title = kwargs['title'] 206 del kwargs['title'] 207 except KeyError: 208 title = None 209 210 try: 211 msg = kwargs['msg'] 212 del kwargs['msg'] 213 except KeyError: 214 msg = None 215 216 try: 217 data = kwargs['data'] 218 del kwargs['data'] 219 except KeyError: 220 data = None 221 222 try: 223 self.original_text = kwargs['text'] 224 del kwargs['text'] 225 except KeyError: 226 self.original_text = None 227 228 wxgMultilineTextEntryDlg.wxgMultilineTextEntryDlg.__init__(self, *args, **kwargs) 229 230 if title is not None: 231 self.SetTitle(title) 232 233 if self.original_text is not None: 234 self._TCTRL_text.SetValue(self.original_text) 235 self._BTN_restore.Enable(True) 236 237 if msg is None: 238 self._LBL_msg.Hide() 239 else: 240 self._LBL_msg.SetLabel(msg) 241 self.Layout() 242 self.Refresh() 243 244 if data is None: 245 self._TCTRL_data.Hide() 246 else: 247 self._TCTRL_data.SetValue(data) 248 self.Layout() 249 self.Refresh() 250 251 self._TCTRL_text.SetFocus()
252 #-------------------------------------------------------- 253 # properties 254 #--------------------------------------------------------
255 - def _get_value(self):
256 return self._TCTRL_text.GetValue()
257 258 value = property(_get_value, lambda x:x) 259 #--------------------------------------------------------
260 - def _get_is_user_formatted(self):
261 return self._CHBOX_is_already_formatted.IsChecked()
262 263 is_user_formatted = property(_get_is_user_formatted, lambda x:x) 264 #--------------------------------------------------------
265 - def _set_enable_user_formatting(self, value):
266 self._CHBOX_is_already_formatted.Enable(value)
267 268 enable_user_formatting = property(lambda x:x, _set_enable_user_formatting) 269 #-------------------------------------------------------- 270 # event handlers 271 #--------------------------------------------------------
272 - def _on_save_button_pressed(self, evt):
273 274 if self.IsModal(): 275 self.EndModal(wx.ID_SAVE) 276 else: 277 self.Close()
278 #--------------------------------------------------------
279 - def _on_clear_button_pressed(self, evt):
280 self._TCTRL_text.SetValue(u'')
281 #--------------------------------------------------------
282 - def _on_restore_button_pressed(self, evt):
283 if self.original_text is not None: 284 self._TCTRL_text.SetValue(self.original_text)
285 # ======================================================================== 286 from Gnumed.business import gmSurgery 287 from Gnumed.wxGladeWidgets import wxgGreetingEditorDlg 288
289 -class cGreetingEditorDlg(wxgGreetingEditorDlg.wxgGreetingEditorDlg):
290
291 - def __init__(self, *args, **kwargs):
292 wxgGreetingEditorDlg.wxgGreetingEditorDlg.__init__(self, *args, **kwargs) 293 294 self.surgery = gmSurgery.gmCurrentPractice() 295 self._TCTRL_message.SetValue(self.surgery.db_logon_banner)
296 #-------------------------------------------------------- 297 # event handlers 298 #--------------------------------------------------------
299 - def _on_save_button_pressed(self, evt):
300 self.surgery.db_logon_banner = self._TCTRL_message.GetValue().strip() 301 if self.IsModal(): 302 self.EndModal(wx.ID_SAVE) 303 else: 304 self.Close()
305 # ========================================================================
306 -class cTreeExpansionHistoryMixin:
307 """TreeCtrl mixin class to record expansion history."""
308 - def __init__(self):
309 if not isinstance(self, wx.TreeCtrl): 310 raise TypeError('[%s]: mixin can only be applied to wx.TreeCtrl, not [%s]' % (cTreeExpansionHistoryMixin, self.__class__.__name__)) 311 self.expansion_state = {}
312 #-------------------------------------------------------- 313 # public API 314 #--------------------------------------------------------
315 - def snapshot_expansion(self):
316 self.__record_subtree_expansion(start_node_id = self.GetRootItem())
317 #--------------------------------------------------------
318 - def restore_expansion(self):
319 if len(self.expansion_state) == 0: 320 return True 321 self.__restore_subtree_expansion(start_node_id = self.GetRootItem())
322 #--------------------------------------------------------
323 - def print_expansion(self):
324 if len(self.expansion_state) == 0: 325 print "currently no expansion snapshot available" 326 return True 327 print "last snapshot of state of expansion" 328 print "-----------------------------------" 329 print "listing expanded nodes:" 330 for node_id in self.expansion_state.keys(): 331 print "node ID:", node_id 332 print " selected:", self.expansion_state[node_id]
333 #-------------------------------------------------------- 334 # internal API 335 #--------------------------------------------------------
336 - def __record_subtree_expansion(self, start_node_id=None):
337 """This records node expansion states based on the item label. 338 339 A side effect of this is that identically named items can 340 become unduly synchronized in their expand state after a 341 snapshot/restore cycle. 342 343 Better choices might be 344 345 id(item.GetPyData()) or 346 item.GetPyData().get_tree_uid() 347 348 where get_tree_uid(): 349 350 '[%s:%s]' % (self.__class__.__name__, id(self)) 351 352 or some such. This would survive renaming of the item. 353 354 For database items it may be useful to include the 355 primary key which would - contrary to id() - survive 356 reloads from the database. 357 """ 358 # protect against empty tree where not even 359 # a root node exists 360 if not start_node_id.IsOk(): 361 return True 362 363 if not self.IsExpanded(start_node_id): 364 return True 365 366 self.expansion_state[self.GetItemText(start_node_id)] = self.IsSelected(start_node_id) 367 368 child_id, cookie = self.GetFirstChild(start_node_id) 369 while child_id.IsOk(): 370 self.__record_subtree_expansion(start_node_id = child_id) 371 child_id, cookie = self.GetNextChild(start_node_id, cookie) 372 373 return
374 #--------------------------------------------------------
375 - def __restore_subtree_expansion(self, start_node_id=None):
376 start_node_label = self.GetItemText(start_node_id) 377 try: 378 node_selected = self.expansion_state[start_node_label] 379 except KeyError: 380 return 381 382 self.Expand(start_node_id) 383 if node_selected: 384 self.SelectItem(start_node_id) 385 386 child_id, cookie = self.GetFirstChild(start_node_id) 387 while child_id.IsOk(): 388 self.__restore_subtree_expansion(start_node_id = child_id) 389 child_id, cookie = self.GetNextChild(start_node_id, cookie) 390 391 return
392 # ========================================================================
393 -class cFileDropTarget(wx.FileDropTarget):
394 """Generic file drop target class. 395 396 Protocol: 397 Widgets being declared file drop targets 398 must provide the method: 399 400 add_filenames(filenames) 401 """ 402 #-----------------------------------------------
403 - def __init__(self, target):
404 wx.FileDropTarget.__init__(self) 405 self.target = target 406 _log.debug('setting up [%s] as file drop target', target)
407 #-----------------------------------------------
408 - def OnDropFiles(self, x, y, filenames):
409 self.target.add_filenames(filenames)
410 # ========================================================================
411 -def file2scaled_image(filename=None, height=100):
412 img_data = None 413 bitmap = None 414 rescaled_height = height 415 try: 416 img_data = wx.Image(filename, wx.BITMAP_TYPE_ANY) 417 current_width = img_data.GetWidth() 418 current_height = img_data.GetHeight() 419 # if current_width == 0: 420 # current_width = 1 421 # if current_height == 0: 422 # current_height = 1 423 rescaled_width = (float(current_width) / current_height) * rescaled_height 424 img_data.Rescale(rescaled_width, rescaled_height, quality = wx.IMAGE_QUALITY_HIGH) # w, h 425 bitmap = wx.BitmapFromImage(img_data) 426 del img_data 427 except StandardError: 428 _log.exception('cannot load image from [%s]', filename) 429 del img_data 430 del bitmap 431 return None 432 433 return bitmap
434 # ========================================================================
435 -def gm_show_error(aMessage = None, aTitle = None):
436 if aMessage is None: 437 aMessage = _('programmer forgot to specify error message') 438 439 aMessage += _("\n\nPlease consult the error log for all the gory details !") 440 441 if aTitle is None: 442 aTitle = _('generic error message') 443 444 dlg = wx.MessageDialog ( 445 parent = None, 446 message = aMessage, 447 caption = aTitle, 448 style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP 449 ) 450 dlg.ShowModal() 451 dlg.Destroy() 452 return True
453 #-------------------------------------------------------------------------
454 -def gm_show_info(aMessage = None, aTitle = None):
455 if aMessage is None: 456 aMessage = _('programmer forgot to specify info message') 457 458 if aTitle is None: 459 aTitle = _('generic info message') 460 461 dlg = wx.MessageDialog ( 462 parent = None, 463 message = aMessage, 464 caption = aTitle, 465 style = wx.OK | wx.ICON_INFORMATION | wx.STAY_ON_TOP 466 ) 467 dlg.ShowModal() 468 dlg.Destroy() 469 return True
470 #-------------------------------------------------------------------------
471 -def gm_show_warning(aMessage=None, aTitle=None):
472 if aMessage is None: 473 aMessage = _('programmer forgot to specify warning') 474 475 if aTitle is None: 476 aTitle = _('generic warning message') 477 478 dlg = wx.MessageDialog ( 479 parent = None, 480 message = aMessage, 481 caption = aTitle, 482 style = wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP 483 ) 484 dlg.ShowModal() 485 dlg.Destroy() 486 return True
487 #-------------------------------------------------------------------------
488 -def gm_show_question(aMessage='programmer forgot to specify question', aTitle='generic user question dialog', cancel_button=False, question=None, title=None):
489 if cancel_button: 490 style = wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION | wx.STAY_ON_TOP 491 else: 492 style = wx.YES_NO | wx.ICON_QUESTION | wx.STAY_ON_TOP 493 494 if question is None: 495 question = aMessage 496 if title is None: 497 title = aTitle 498 499 dlg = wx.MessageDialog(None, question, title, style) 500 btn_pressed = dlg.ShowModal() 501 dlg.Destroy() 502 503 if btn_pressed == wx.ID_YES: 504 return True 505 elif btn_pressed == wx.ID_NO: 506 return False 507 else: 508 return None
509 510 #====================================================================== 511 if __name__ == '__main__': 512 513 if len(sys.argv) < 2: 514 sys.exit() 515 516 if sys.argv[1] != 'test': 517 sys.exit() 518 519 from Gnumed.pycommon import gmI18N 520 gmI18N.activate_locale() 521 gmI18N.install_domain(domain='gnumed') 522 523 #------------------------------------------------------------------
524 - def test_scale_img():
525 app = wx.App() 526 img = file2scaled_image(filename = sys.argv[2]) 527 print img 528 print img.Height 529 print img.Width
530 #------------------------------------------------------------------
531 - def test_sql_logic_prw():
532 app = wx.PyWidgetTester(size = (200, 50)) 533 prw = cThreeValuedLogicPhraseWheel(parent = app.frame, id = -1) 534 app.frame.Show(True) 535 app.MainLoop() 536 537 return True
538 #------------------------------------------------------------------ 539 #test_scale_img() 540 test_sql_logic_prw() 541 542 #====================================================================== 543