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  import io
     15
     16
     17  import wx
     18
     19
     20  if __name__ == '__main__':
     21          sys.path.insert(0, '../../')
     22  from Gnumed.pycommon import gmMatchProvider
     23  from Gnumed.pycommon import gmExceptions
     24  from Gnumed.pycommon import gmLog2
     25  from Gnumed.pycommon import gmTools
     26  from Gnumed.pycommon import gmDispatcher
     27  from Gnumed.wxpython import gmPhraseWheel
     28
     29
     30  _log = logging.getLogger('gm.main')
     31  # ========================================================================
     32 -class cThreeValuedLogicPhraseWheel(gmPhraseWheel.cPhraseWheel):
     33
     34 -        def __init__(self, *args, **kwargs):
     35
     36                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     37
     38                  items = [
     39                          {'list_label': _('Yes: + / ! / 1'), 'field_label': _('yes'), 'data': True, 'weight': 0},
     40                          {'list_label': _('No: - / 0'), 'field_label': _('no'), 'data': False, 'weight': 1},
     41                          {'list_label': _('Unknown: ?'), 'field_label': _('unknown'), 'data': None, 'weight': 2},
     42                  ]
     43                  mp = gmMatchProvider.cMatchProvider_FixedList(items)
     44                  mp.setThresholds(1, 1, 2)
     45                  mp.word_separators = '[ :/]+'
     46                  mp.word_separators = None
     47                  mp.ignored_chars = r"[.'\\(){}\[\]<>~#*$%^_=&@\t23456]+" + r'"'
     48
     49                  self.matcher = mp
     50  # ========================================================================
     51  from Gnumed.wxGladeWidgets import wxg2ButtonQuestionDlg
     52
     53 -class c2ButtonQuestionDlg(wxg2ButtonQuestionDlg.wxg2ButtonQuestionDlg):
     54
     55 -        def __init__(self, *args, **kwargs):
     56
     57                  caption = kwargs['caption']
     58                  question = kwargs['question']
     59                  button_defs = kwargs['button_defs'][:2]
     60                  del kwargs['caption']
     61                  del kwargs['question']
     62                  del kwargs['button_defs']
     63
     64                  try:
     65                          show_checkbox = kwargs['show_checkbox']
     66                          del kwargs['show_checkbox']
     67                  except KeyError:
     68                          show_checkbox = False
     69
     70                  try:
     71                          checkbox_msg = kwargs['checkbox_msg']
     72                          del kwargs['checkbox_msg']
     73                  except KeyError:
     74                          checkbox_msg = None
     75
     76                  try:
     77                          checkbox_tooltip = kwargs['checkbox_tooltip']
     78                          del kwargs['checkbox_tooltip']
     79                  except KeyError:
     80                          checkbox_tooltip = None
     81
     82                  wxg2ButtonQuestionDlg.wxg2ButtonQuestionDlg.__init__(self, *args, **kwargs)
     83
     84                  if not caption.startswith('GMd: '):
     85                          caption = 'GMd: %s' % caption
     86                  self.SetTitle(title = caption)
     87                  self._LBL_question.SetLabel(label = question)
     88
     89                  if not show_checkbox:
     90                          self._CHBOX_dont_ask_again.Hide()
     91                  else:
     92                          if checkbox_msg is not None:
     93                                  self._CHBOX_dont_ask_again.SetLabel(checkbox_msg)
     94                          if checkbox_tooltip is not None:
     95                                  self._CHBOX_dont_ask_again.SetToolTip(checkbox_tooltip)
     96
     97                  buttons = [self._BTN_1, self._BTN_2]
     98                  for idx in range(len(button_defs)):
     99                          buttons[idx].SetLabel(label = button_defs[idx]['label'])
    100                          buttons[idx].SetToolTip(button_defs[idx]['tooltip'])
    101                          try:
    102                                  if button_defs[idx]['default'] is True:
    103                                          buttons[idx].SetDefault()
    104                                          buttons[idx].SetFocus()
    105                          except KeyError:
    106                                  pass
    107
    108                  self.Fit()
    109          #--------------------------------------------------------
    110 -        def checkbox_is_checked(self):
    111                  return self._CHBOX_dont_ask_again.IsChecked()
    112          #--------------------------------------------------------
    113          # event handlers
    114          #--------------------------------------------------------
    115 -        def _on_BTN_1_pressed(self, evt):
    116                  if self.IsModal():
    117                          self.EndModal(wx.ID_YES)
    118                  else:
    119                          self.Close()
    120          #--------------------------------------------------------
    121 -        def _on_BTN_2_pressed(self, evt):
    122                  if self.IsModal():
    123                          self.EndModal(wx.ID_NO)
    124                  else:
    125                          self.Close()
    126
    127  # ========================================================================
    128  from Gnumed.wxGladeWidgets import wxg3ButtonQuestionDlg
    129
    130 -class c3ButtonQuestionDlg(wxg3ButtonQuestionDlg.wxg3ButtonQuestionDlg):
    131
    132 -        def __init__(self, *args, **kwargs):
    133                  """Initialize.
    134
    135          button_defs = [
    136                  # tooltip and default are optional
    137                  {'label': _(''), 'tooltip': _(''), 'default': True/False},
    138                  {'label': _(''), 'tooltip': _(''), 'default': True/False},
    139                  {'label': _(''), 'tooltip': _(''), 'default': True/False}
    140          ]
    141                  """
    142                  caption = kwargs['caption']
    143                  question = kwargs['question']
    144                  button_defs = kwargs['button_defs'][:3]
    145                  del kwargs['caption']
    146                  del kwargs['question']
    147                  del kwargs['button_defs']
    148
    149                  try:
    150                          show_checkbox = kwargs['show_checkbox']
    151                          del kwargs['show_checkbox']
    152                  except KeyError:
    153                          show_checkbox = False
    154
    155                  try:
    156                          checkbox_msg = kwargs['checkbox_msg']
    157                          del kwargs['checkbox_msg']
    158                  except KeyError:
    159                          checkbox_msg = None
    160
    161                  try:
    162                          checkbox_tooltip = kwargs['checkbox_tooltip']
    163                          del kwargs['checkbox_tooltip']
    164                  except KeyError:
    165                          checkbox_tooltip = None
    166
    167                  wxg3ButtonQuestionDlg.wxg3ButtonQuestionDlg.__init__(self, *args, **kwargs)
    168
    169                  if not caption.startswith('GMd: '):
    170                          caption = 'GMd: %s' % caption
    171                  self.SetTitle(title = caption)
    172                  self._LBL_question.SetLabel(label = question)
    173
    174                  if not show_checkbox:
    175                          self._CHBOX_dont_ask_again.Hide()
    176                  else:
    177                          if checkbox_msg is not None:
    178                                  self._CHBOX_dont_ask_again.SetLabel(checkbox_msg)
    179                          if checkbox_tooltip is not None:
    180                                  self._CHBOX_dont_ask_again.SetToolTip(checkbox_tooltip)
    181
    182                  buttons = [self._BTN_1, self._BTN_2, self._BTN_3]
    183                  for idx in range(len(button_defs)):
    184                          buttons[idx].SetLabel(label = button_defs[idx]['label'])
    185                          try:
    186                                  buttons[idx].SetToolTip(button_defs[idx]['tooltip'])
    187                          except KeyError:
    188                                  pass
    189                          try:
    190                                  if button_defs[idx]['default'] is True:
    191                                          buttons[idx].SetDefault()
    192                                          buttons[idx].SetFocus()
    193                          except KeyError:
    194                                  pass
    195
    196                  self.Fit()
    197          #--------------------------------------------------------
    198 -        def checkbox_is_checked(self):
    199                  return self._CHBOX_dont_ask_again.IsChecked()
    200
    201          #--------------------------------------------------------
    202          # event handlers
    203          #--------------------------------------------------------
    204 -        def _on_BTN_1_pressed(self, evt):
    205                  if self.IsModal():
    206                          self.EndModal(wx.ID_YES)
    207                  else:
    208                          self.Close()
    209
    210          #--------------------------------------------------------
    211 -        def _on_BTN_2_pressed(self, evt):
    212                  if self.IsModal():
    213                          self.EndModal(wx.ID_NO)
    214                  else:
    215                          self.Close()
    216
    217  # ========================================================================
    218  from Gnumed.wxGladeWidgets import wxgMultilineTextEntryDlg
    219
    220 -class cMultilineTextEntryDlg(wxgMultilineTextEntryDlg.wxgMultilineTextEntryDlg):
    221          """Editor for a bit of text."""
    222
    223 -        def __init__(self, *args, **kwargs):
    224
    225                  try:
    226                          title = kwargs['title']
    227                          del kwargs['title']
    228                  except KeyError:
    229                          title = None
    230
    231                  try:
    232                          msg = kwargs['msg']
    233                          del kwargs['msg']
    234                  except KeyError:
    235                          msg = None
    236
    237                  try:
    238                          data = kwargs['data']
    239                          del kwargs['data']
    240                  except KeyError:
    241                          data = None
    242
    243                  try:
    244                          self.original_text = kwargs['text']
    245                          del kwargs['text']
    246                  except KeyError:
    247                          self.original_text = None
    248
    249                  wxgMultilineTextEntryDlg.wxgMultilineTextEntryDlg.__init__(self, *args, **kwargs)
    250
    251                  if title is not None:
    252                          if not title.startswith('GMd: '):
    253                                  title = 'GMd: %s' % title
    254                          self.SetTitle(title)
    255
    256                  if self.original_text is not None:
    257                          self._TCTRL_text.SetValue(self.original_text)
    258                          self._BTN_restore.Enable(True)
    259
    260                  if msg is None:
    261                          self._LBL_msg.Hide()
    262                  else:
    263                          self._LBL_msg.SetLabel(msg)
    264                          self.Layout()
    265                          self.Refresh()
    266
    267                  if data is None:
    268                          self._TCTRL_data.Hide()
    269                  else:
    270                          self._TCTRL_data.SetValue(data)
    271                          self.Layout()
    272                          self.Refresh()
    273
    274                  self._TCTRL_text.SetFocus()
    275          #--------------------------------------------------------
    276          # properties
    277          #--------------------------------------------------------
    278 -        def _get_value(self):
    279                  return self._TCTRL_text.GetValue()
    280
    281          value = property(_get_value, lambda x:x)
    282          #--------------------------------------------------------
    283 -        def _get_is_user_formatted(self):
    284                  return self._CHBOX_is_already_formatted.IsChecked()
    285
    286          is_user_formatted = property(_get_is_user_formatted, lambda x:x)
    287          #--------------------------------------------------------
    288 -        def _set_enable_user_formatting(self, value):
    289                  self._CHBOX_is_already_formatted.Enable(value)
    290
    291          enable_user_formatting = property(lambda x:x, _set_enable_user_formatting)
    292          #--------------------------------------------------------
    293          # event handlers
    294          #--------------------------------------------------------
    295 -        def _on_save_button_pressed(self, evt):
    296
    297                  if self.IsModal():
    298                          self.EndModal(wx.ID_SAVE)
    299                  else:
    300                          self.Close()
    301          #--------------------------------------------------------
    302 -        def _on_clear_button_pressed(self, evt):
    303                  self._TCTRL_text.SetValue('')
    304          #--------------------------------------------------------
    305 -        def _on_restore_button_pressed(self, evt):
    306                  if self.original_text is not None:
    307                          self._TCTRL_text.SetValue(self.original_text)
    308
    309  # ========================================================================
    310 -def clipboard2text():
    311
    312          if wx.TheClipboard.IsOpened():
    313                  return False
    314
    315          if not wx.TheClipboard.Open():
    316                  return False
    317
    318          data_obj = wx.TextDataObject()
    319          got_it = wx.TheClipboard.GetData(data_obj)
    320          if got_it:
    321                  txt = data_obj.Text
    322                  wx.TheClipboard.Close()
    323                  return txt
    324
    325          wx.TheClipboard.Close()
    326          return None
    327
    328  #-------------------------------------------------------------------------
    329 -def clipboard2file(check_for_filename=False):
    330
    331          if wx.TheClipboard.IsOpened():
    332                  return False
    333
    334          if not wx.TheClipboard.Open():
    335                  return False
    336
    337          data_obj = wx.TextDataObject()
    338          got_it = wx.TheClipboard.GetData(data_obj)
    339          if got_it:
    340                  clipboard_text_content = data_obj.Text
    341                  wx.TheClipboard.Close()
    342                  if check_for_filename:
    343                          try:
    344                                  io.open(clipboard_text_content).close()
    345                                  return clipboard_text_content
    346                          except IOError:
    347                                  _log.exception('clipboard does not seem to hold filename: %s', clipboard_text_content)
    348                  fname = gmTools.get_unique_filename(prefix = 'gm-clipboard-', suffix = '.txt')
    349                  target_file = io.open(fname, mode = 'wt', encoding = 'utf8')
    350                  target_file.write(clipboard_text_content)
    351                  target_file.close()
    352                  return fname
    353
    354          data_obj = wx.BitmapDataObject()
    355          got_it = wx.TheClipboard.GetData(data_obj)
    356          if got_it:
    357                  fname = gmTools.get_unique_filename(prefix = 'gm-clipboard-', suffix = '.png')
    358                  bmp = data_obj.Bitmap.SaveFile(fname, wx.BITMAP_TYPE_PNG)
    359                  wx.TheClipboard.Close()
    360                  return fname
    361
    362          wx.TheClipboard.Close()
    363          return None
    364
    365  #-------------------------------------------------------------------------
    366 -def text2clipboard(text=None, announce_result=False):
    367          if wx.TheClipboard.IsOpened():
    368                  return False
    369          if not wx.TheClipboard.Open():
    370                  return False
    371          data_obj = wx.TextDataObject()
    372          data_obj.SetText(text)
    373          wx.TheClipboard.SetData(data_obj)
    374          wx.TheClipboard.Close()
    375          if announce_result:
    376                  gmDispatcher.send(signal = 'statustext', msg = _('The text has been copied into the clipboard.'), beep = False)
    377          return True
    378
    379  #-------------------------------------------------------------------------
    380 -def file2clipboard(filename=None, announce_result=False):
    381          f = io.open(filename, mode = 'rt', encoding = 'utf8')
    382          result = text2clipboard(text = f.read(), announce_result = False)
    383          f.close()
    384          if announce_result:
    385                  gm_show_info (
    386                          title = _('file2clipboard'),
    387                          info = _('The file [%s] has been copied into the clipboard.') % filename
    388                  )
    389          return result
    390
    391  # ========================================================================
    392 -class cFileDropTarget(wx.FileDropTarget):
    393          """Generic file drop target class.
    394
    395          Protocol:
    396                  Widgets being declared file drop targets
    397                  must provide the method:
    398
    399                          def _drop_target_consume_filenames(self, filenames)
    400
    401                  or declare a callback during __init__() of this class.
    402          """
    403          #-----------------------------------------------
    404 -        def __init__(self, target=None, on_drop_callback=None):
    405                  if target is not None:
    406                          try:
    407                                  on_drop_callback = getattr(target, '_drop_target_consume_filenames')
    408                          except AttributeError:
    409                                  _log.exception('[%s._drop_target_consume_filenames()] does not exist, cannot set as drop target callback', target)
    410                                  raise
    411                  if not callable(on_drop_callback):
    412                          _log.error('[%s] not callable, cannot set as drop target callback', on_drop_callback)
    413                          raise AttributeError('[%s] not callable, cannot set as drop target callback', on_drop_callback)
    414                  self._on_drop_callback = on_drop_callback
    415                  wx.FileDropTarget.__init__(self)
    416                  _log.debug('setting up [%s] as file drop target', self._on_drop_callback)
    417
    418          #-----------------------------------------------
    419 -        def OnDropFiles(self, x, y, filenames):
    420                  self._on_drop_callback(filenames)
    421
    422  # ========================================================================
    423 -def file2scaled_image(filename=None, height=100):
    424          img_data = None
    425          bitmap = None
    426          rescaled_height = height
    427          try:
    428                  img_data = wx.Image(filename, wx.BITMAP_TYPE_ANY)
    429                  current_width = img_data.GetWidth()
    430                  current_height = img_data.GetHeight()
    431  #               if current_width == 0:
    432  #                       current_width = 1
    433  #               if current_height == 0:
    434  #                       current_height = 1
    435                  rescaled_width = (float(current_width) / current_height) * rescaled_height
    436                  img_data.Rescale(rescaled_width, rescaled_height, quality = wx.IMAGE_QUALITY_HIGH)              # w, h
    437                  bitmap = wx.Bitmap(img_data)
    438                  del img_data
    439          except Exception:
    440                  _log.exception('cannot load image from [%s]', filename)
    441                  del img_data
    442                  del bitmap
    443                  return None
    444          return bitmap
    445
    446  # ========================================================================
    447 -def gm_show_error(aMessage=None, aTitle = None, error=None, title=None):
    448
    449          if error is None:
    450                  error = aMessage
    451          if error is None:
    452                  error = _('programmer forgot to specify error message')
    453          error += _("\n\nPlease consult the error log for all the gory details !")
    454
    455          if title is None:
    456                  title = aTitle
    457          if title is None:
    458                  title = _('generic error message')
    459          if not title.startswith('GMd: '):
    460                  title = 'GMd: %s' % title
    461
    462          dlg = wx.MessageDialog (
    463                  parent = None,
    464                  message = error,
    465                  caption = title,
    466                  style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP
    467          )
    468          dlg.ShowModal()
    469          dlg.DestroyLater()
    470          return True
    471
    472  #-------------------------------------------------------------------------
    473 -def gm_show_info(aMessage=None, aTitle=None, info=None, title=None):
    474
    475          if info is None:
    476                  info = aMessage
    477          if info is None:
    478                  info = _('programmer forgot to specify info message')
    479
    480          if title is None:
    481                  title = aTitle
    482          if title is None:
    483                  title = _('generic info message')
    484          if not title.startswith('GMd: '):
    485                  title = 'GMd: %s' % title
    486
    487          dlg = wx.MessageDialog (
    488                  parent = None,
    489                  message = info,
    490                  caption = title,
    491                  style = wx.OK | wx.ICON_INFORMATION | wx.STAY_ON_TOP
    492          )
    493          dlg.ShowModal()
    494          dlg.DestroyLater()
    495          return True
    496
    497  #-------------------------------------------------------------------------
    498 -def gm_show_warning(aMessage=None, aTitle=None):
    499          if aMessage is None:
    500                  aMessage = _('programmer forgot to specify warning')
    501
    502          if aTitle is None:
    503                  aTitle = _('generic warning message')
    504          if not aTitle.startswith('GMd: '):
    505                  aTitle = 'GMd: %s' % aTitle
    506
    507          dlg = wx.MessageDialog (
    508                  parent = None,
    509                  message = aMessage,
    510                  caption = aTitle,
    511                  style = wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP
    512          )
    513          dlg.ShowModal()
    514          dlg.DestroyLater()
    515          return True
    516
    517  #-------------------------------------------------------------------------
    518 -def gm_show_question(aMessage='programmer forgot to specify question', aTitle='generic user question dialog', cancel_button=False, question=None, title=None):
    519          if cancel_button:
    520                  style = wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION | wx.STAY_ON_TOP
    521          else:
    522                  style = wx.YES_NO | wx.ICON_QUESTION | wx.STAY_ON_TOP
    523
    524          if question is None:
    525                  question = aMessage
    526          if title is None:
    527                  title = aTitle
    528          if not title.startswith('GMd: '):
    529                  title = 'GMd: %s' % title
    530
    531          dlg = wx.MessageDialog(None, question, title, style)
    532          btn_pressed = dlg.ShowModal()
    533          dlg.DestroyLater()
    534
    535          if btn_pressed == wx.ID_YES:
    536                  return True
    537          elif btn_pressed == wx.ID_NO:
    538                  return False
    539          else:
    540                  return None
    541
    542  #======================================================================
    543  if __name__ == '__main__':
    544
    545          if len(sys.argv) < 2:
    546                  sys.exit()
    547
    548          if sys.argv[1] != 'test':
    549                  sys.exit()
    550
    551          from Gnumed.pycommon import gmI18N
    552          gmI18N.activate_locale()
    553          gmI18N.install_domain(domain='gnumed')
    554
    555          #------------------------------------------------------------------
    556 -        def test_scale_img():
    557                  app = wx.App()
    558                  img = file2scaled_image(filename = sys.argv[2])
    559                  print(img)
    560                  print(img.Height)
    561                  print(img.Width)
    562          #------------------------------------------------------------------
    563 -        def test_sql_logic_prw():
    564                  app = wx.PyWidgetTester(size = (200, 50))
    565                  prw = cThreeValuedLogicPhraseWheel(app.frame, -1)
    566                  app.frame.Show(True)
    567                  app.MainLoop()
    568
    569                  return True
    570          #------------------------------------------------------------------
    571 -        def test_clipboard():
    572                  app = wx.PyWidgetTester(size = (200, 50))
    573                  result = clipboard2file()
    574                  if result is False:
    575                          print("problem opening clipboard")
    576                          return
    577                  if result is None:
    578                          print("no data in clipboard")
    579                          return
    580                  print("file:", result)
    581          #------------------------------------------------------------------
    582          #test_scale_img()
    583          #test_sql_logic_prw()
    584          test_clipboard()
    585
    586  #======================================================================
    587