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
33
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
126
127
128 from Gnumed.wxGladeWidgets import wxg3ButtonQuestionDlg
129
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
277
278 - def _get_value(self):
279 return self._TCTRL_text.GetValue()
280
281 value = property(_get_value, lambda x:x)
282
284 return self._CHBOX_is_already_formatted.IsChecked()
285
286 is_user_formatted = property(_get_is_user_formatted, lambda x:x)
287
290
291 enable_user_formatting = property(lambda x:x, _set_enable_user_formatting)
292
293
294
296
297 if self.IsModal():
298 self.EndModal(wx.ID_SAVE)
299 else:
300 self.Close()
301
304
306 if self.original_text is not None:
307 self._TCTRL_text.SetValue(self.original_text)
308
309
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
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
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
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
420 self._on_drop_callback(filenames)
421
422
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
432
433
434
435 rescaled_width = (float(current_width) / current_height) * rescaled_height
436 img_data.Rescale(rescaled_width, rescaled_height, quality = wx.IMAGE_QUALITY_HIGH)
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
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
557 app = wx.App()
558 img = file2scaled_image(filename = sys.argv[2])
559 print(img)
560 print(img.Height)
561 print(img.Width)
562
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
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
583
584 test_clipboard()
585
586
587