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