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 # ========================================================================
287 -class cTreeExpansionHistoryMixin:
288 """TreeCtrl mixin class to record expansion history."""
289 - def __init__(self):
290 if not isinstance(self, wx.TreeCtrl): 291 raise TypeError('[%s]: mixin can only be applied to wx.TreeCtrl, not [%s]' % (cTreeExpansionHistoryMixin, self.__class__.__name__)) 292 self.expansion_state = {}
293 #-------------------------------------------------------- 294 # public API 295 #--------------------------------------------------------
296 - def snapshot_expansion(self):
297 self.__record_subtree_expansion(start_node_id = self.GetRootItem())
298 #--------------------------------------------------------
299 - def restore_expansion(self):
300 if len(self.expansion_state) == 0: 301 return True 302 self.__restore_subtree_expansion(start_node_id = self.GetRootItem())
303 #--------------------------------------------------------
304 - def print_expansion(self):
305 if len(self.expansion_state) == 0: 306 print "currently no expansion snapshot available" 307 return True 308 print "last snapshot of state of expansion" 309 print "-----------------------------------" 310 print "listing expanded nodes:" 311 for node_id in self.expansion_state.keys(): 312 print "node ID:", node_id 313 print " selected:", self.expansion_state[node_id]
314 #-------------------------------------------------------- 315 # internal API 316 #--------------------------------------------------------
317 - def __record_subtree_expansion(self, start_node_id=None):
318 """This records node expansion states based on the item label. 319 320 A side effect of this is that identically named items can 321 become unduly synchronized in their expand state after a 322 snapshot/restore cycle. 323 324 Better choices might be 325 326 id(item.GetPyData()) or 327 item.GetPyData().get_tree_uid() 328 329 where get_tree_uid(): 330 331 '[%s:%s]' % (self.__class__.__name__, id(self)) 332 333 or some such. This would survive renaming of the item. 334 335 For database items it may be useful to include the 336 primary key which would - contrary to id() - survive 337 reloads from the database. 338 """ 339 # protect against empty tree where not even 340 # a root node exists 341 if not start_node_id.IsOk(): 342 return True 343 344 if not self.IsExpanded(start_node_id): 345 return True 346 347 self.expansion_state[self.GetItemText(start_node_id)] = self.IsSelected(start_node_id) 348 349 child_id, cookie = self.GetFirstChild(start_node_id) 350 while child_id.IsOk(): 351 self.__record_subtree_expansion(start_node_id = child_id) 352 child_id, cookie = self.GetNextChild(start_node_id, cookie) 353 354 return
355 #--------------------------------------------------------
356 - def __restore_subtree_expansion(self, start_node_id=None):
357 start_node_label = self.GetItemText(start_node_id) 358 try: 359 node_selected = self.expansion_state[start_node_label] 360 except KeyError: 361 return 362 363 self.Expand(start_node_id) 364 if node_selected: 365 self.SelectItem(start_node_id) 366 367 child_id, cookie = self.GetFirstChild(start_node_id) 368 while child_id.IsOk(): 369 self.__restore_subtree_expansion(start_node_id = child_id) 370 child_id, cookie = self.GetNextChild(start_node_id, cookie) 371 372 return
373 # ========================================================================
374 -class cFileDropTarget(wx.FileDropTarget):
375 """Generic file drop target class. 376 377 Protocol: 378 Widgets being declared file drop targets 379 must provide the method: 380 381 add_filenames(filenames) 382 """ 383 #-----------------------------------------------
384 - def __init__(self, target):
385 wx.FileDropTarget.__init__(self) 386 self.target = target 387 _log.debug('setting up [%s] as file drop target', target)
388 #-----------------------------------------------
389 - def OnDropFiles(self, x, y, filenames):
390 self.target.add_filenames(filenames)
391 # ========================================================================
392 -def file2scaled_image(filename=None, height=100):
393 img_data = None 394 bitmap = None 395 rescaled_height = height 396 try: 397 img_data = wx.Image(filename, wx.BITMAP_TYPE_ANY) 398 current_width = img_data.GetWidth() 399 current_height = img_data.GetHeight() 400 # if current_width == 0: 401 # current_width = 1 402 # if current_height == 0: 403 # current_height = 1 404 rescaled_width = (float(current_width) / current_height) * rescaled_height 405 img_data.Rescale(rescaled_width, rescaled_height, quality = wx.IMAGE_QUALITY_HIGH) # w, h 406 bitmap = wx.BitmapFromImage(img_data) 407 del img_data 408 except StandardError: 409 _log.exception('cannot load image from [%s]', filename) 410 del img_data 411 del bitmap 412 return None 413 414 return bitmap
415 # ========================================================================
416 -def gm_show_error(aMessage=None, aTitle = None, error=None, title=None):
417 418 if error is None: 419 error = aMessage 420 if error is None: 421 error = _('programmer forgot to specify error message') 422 error += _("\n\nPlease consult the error log for all the gory details !") 423 424 if title is None: 425 title = aTitle 426 if title is None: 427 title = _('generic error message') 428 429 dlg = wx.MessageDialog ( 430 parent = None, 431 message = error, 432 caption = title, 433 style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP 434 ) 435 dlg.ShowModal() 436 dlg.Destroy() 437 return True
438 #-------------------------------------------------------------------------
439 -def gm_show_info(aMessage=None, aTitle=None, info=None, title=None):
440 441 if info is None: 442 info = aMessage 443 if info is None: 444 info = _('programmer forgot to specify info message') 445 446 if title is None: 447 title = aTitle 448 if title is None: 449 title = _('generic info message') 450 451 dlg = wx.MessageDialog ( 452 parent = None, 453 message = info, 454 caption = title, 455 style = wx.OK | wx.ICON_INFORMATION | wx.STAY_ON_TOP 456 ) 457 dlg.ShowModal() 458 dlg.Destroy() 459 return True
460 #-------------------------------------------------------------------------
461 -def gm_show_warning(aMessage=None, aTitle=None):
462 if aMessage is None: 463 aMessage = _('programmer forgot to specify warning') 464 465 if aTitle is None: 466 aTitle = _('generic warning message') 467 468 dlg = wx.MessageDialog ( 469 parent = None, 470 message = aMessage, 471 caption = aTitle, 472 style = wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP 473 ) 474 dlg.ShowModal() 475 dlg.Destroy() 476 return True
477 #-------------------------------------------------------------------------
478 -def gm_show_question(aMessage='programmer forgot to specify question', aTitle='generic user question dialog', cancel_button=False, question=None, title=None):
479 if cancel_button: 480 style = wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION | wx.STAY_ON_TOP 481 else: 482 style = wx.YES_NO | wx.ICON_QUESTION | wx.STAY_ON_TOP 483 484 if question is None: 485 question = aMessage 486 if title is None: 487 title = aTitle 488 489 dlg = wx.MessageDialog(None, question, title, style) 490 btn_pressed = dlg.ShowModal() 491 dlg.Destroy() 492 493 if btn_pressed == wx.ID_YES: 494 return True 495 elif btn_pressed == wx.ID_NO: 496 return False 497 else: 498 return None
499 500 #====================================================================== 501 if __name__ == '__main__': 502 503 if len(sys.argv) < 2: 504 sys.exit() 505 506 if sys.argv[1] != 'test': 507 sys.exit() 508 509 from Gnumed.pycommon import gmI18N 510 gmI18N.activate_locale() 511 gmI18N.install_domain(domain='gnumed') 512 513 #------------------------------------------------------------------
514 - def test_scale_img():
515 app = wx.App() 516 img = file2scaled_image(filename = sys.argv[2]) 517 print img 518 print img.Height 519 print img.Width
520 #------------------------------------------------------------------
521 - def test_sql_logic_prw():
522 app = wx.PyWidgetTester(size = (200, 50)) 523 prw = cThreeValuedLogicPhraseWheel(parent = app.frame, id = -1) 524 app.frame.Show(True) 525 app.MainLoop() 526 527 return True
528 #------------------------------------------------------------------ 529 #test_scale_img() 530 test_sql_logic_prw() 531 532 #====================================================================== 533