Gnumed.wxpython.gmGuiHelpers

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