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
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
264
265 - def _get_value(self):
266 return self._TCTRL_text.GetValue()
267
268 value = property(_get_value, lambda x:x)
269
271 return self._CHBOX_is_already_formatted.IsChecked()
272
273 is_user_formatted = property(_get_is_user_formatted, lambda x:x)
274
277
278 enable_user_formatting = property(lambda x:x, _set_enable_user_formatting)
279
280
281
283
284 if self.IsModal():
285 self.EndModal(wx.ID_SAVE)
286 else:
287 self.Close()
288
291
293 if self.original_text is not None:
294 self._TCTRL_text.SetValue(self.original_text)
295
296
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
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
377
378
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
407 self._on_drop_callback(filenames)
408
409
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
419
420
421
422 rescaled_width = (float(current_width) / current_height) * rescaled_height
423 img_data.Rescale(rescaled_width, rescaled_height, quality = wx.IMAGE_QUALITY_HIGH)
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
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
544 app = wx.App()
545 img = file2scaled_image(filename = sys.argv[2])
546 print(img)
547 print(img.Height)
548 print(img.Width)
549
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
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
570
571 test_clipboard()
572
573
574