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
124
125
126 from Gnumed.wxGladeWidgets import wxg3ButtonQuestionDlg
127
199
200
201 from Gnumed.wxGladeWidgets import wxgMultilineTextEntryDlg
202
203 -class cMultilineTextEntryDlg(wxgMultilineTextEntryDlg.wxgMultilineTextEntryDlg):
204 """Editor for a bit of text."""
205
206 - def __init__(self, *args, **kwargs):
207
208 try:
209 title = kwargs['title']
210 del kwargs['title']
211 except KeyError:
212 title = None
213
214 try:
215 msg = kwargs['msg']
216 del kwargs['msg']
217 except KeyError:
218 msg = None
219
220 try:
221 data = kwargs['data']
222 del kwargs['data']
223 except KeyError:
224 data = None
225
226 try:
227 self.original_text = kwargs['text']
228 del kwargs['text']
229 except KeyError:
230 self.original_text = None
231
232 wxgMultilineTextEntryDlg.wxgMultilineTextEntryDlg.__init__(self, *args, **kwargs)
233
234 if title is not None:
235 self.SetTitle(title)
236
237 if self.original_text is not None:
238 self._TCTRL_text.SetValue(self.original_text)
239 self._BTN_restore.Enable(True)
240
241 if msg is None:
242 self._LBL_msg.Hide()
243 else:
244 self._LBL_msg.SetLabel(msg)
245 self.Layout()
246 self.Refresh()
247
248 if data is None:
249 self._TCTRL_data.Hide()
250 else:
251 self._TCTRL_data.SetValue(data)
252 self.Layout()
253 self.Refresh()
254
255 self._TCTRL_text.SetFocus()
256
257
258
259 - def _get_value(self):
260 return self._TCTRL_text.GetValue()
261
262 value = property(_get_value, lambda x:x)
263
265 return self._CHBOX_is_already_formatted.IsChecked()
266
267 is_user_formatted = property(_get_is_user_formatted, lambda x:x)
268
271
272 enable_user_formatting = property(lambda x:x, _set_enable_user_formatting)
273
274
275
277
278 if self.IsModal():
279 self.EndModal(wx.ID_SAVE)
280 else:
281 self.Close()
282
285
287 if self.original_text is not None:
288 self._TCTRL_text.SetValue(self.original_text)
289
290
292
293 if wx.TheClipboard.IsOpened():
294 return False
295
296 if not wx.TheClipboard.Open():
297 return False
298
299 data_obj = wx.TextDataObject()
300 got_it = wx.TheClipboard.GetData(data_obj)
301 if got_it:
302 txt = data_obj.Text
303 wx.TheClipboard.Close()
304 return txt
305
306 wx.TheClipboard.Close()
307 return None
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 clipboard_text_content = data_obj.Text
322 wx.TheClipboard.Close()
323 if check_for_filename:
324 try:
325 io.open(clipboard_text_content).close()
326 return clipboard_text_content
327 except IOError:
328 _log.exception('clipboard does not seem to hold filename: %s', clipboard_text_content)
329 fname = gmTools.get_unique_filename(prefix = 'gm-clipboard-', suffix = '.txt')
330 target_file = io.open(fname, mode = 'wt', encoding = 'utf8')
331 target_file.write(clipboard_text_content)
332 target_file.close()
333 return fname
334
335 data_obj = wx.BitmapDataObject()
336 got_it = wx.TheClipboard.GetData(data_obj)
337 if got_it:
338 fname = gmTools.get_unique_filename(prefix = 'gm-clipboard-', suffix = '.png')
339 bmp = data_obj.Bitmap.SaveFile(fname, wx.BITMAP_TYPE_PNG)
340 wx.TheClipboard.Close()
341 return fname
342
343 wx.TheClipboard.Close()
344 return None
345
346
347 -def text2clipboard(text=None, announce_result=False):
348 if wx.TheClipboard.IsOpened():
349 return False
350 if not wx.TheClipboard.Open():
351 return False
352 data_obj = wx.TextDataObject()
353 data_obj.SetText(text)
354 wx.TheClipboard.SetData(data_obj)
355 wx.TheClipboard.Close()
356 if announce_result:
357 gmDispatcher.send(signal = 'statustext', msg = _('The text has been copied into the clipboard.'), beep = False)
358 return True
359
360
371
372
374 """Generic file drop target class.
375
376 Protocol:
377 Widgets being declared file drop targets
378 must provide the method:
379
380 def _drop_target_consume_filenames(self, filenames)
381
382 or declare a callback during __init__() of this class.
383 """
384
385 - def __init__(self, target=None, on_drop_callback=None):
386 if target is not None:
387 try:
388 on_drop_callback = getattr(target, '_drop_target_consume_filenames')
389 except AttributeError:
390 _log.exception('[%s._drop_target_consume_filenames()] does not exist, cannot set as drop target callback', target)
391 raise
392 if not callable(on_drop_callback):
393 _log.error('[%s] not callable, cannot set as drop target callback', on_drop_callback)
394 raise AttributeError('[%s] not callable, cannot set as drop target callback', on_drop_callback)
395 self._on_drop_callback = on_drop_callback
396 wx.FileDropTarget.__init__(self)
397 _log.debug('setting up [%s] as file drop target', self._on_drop_callback)
398
399
401 self._on_drop_callback(filenames)
402
403
405 img_data = None
406 bitmap = None
407 rescaled_height = height
408 try:
409 img_data = wx.Image(filename, wx.BITMAP_TYPE_ANY)
410 current_width = img_data.GetWidth()
411 current_height = img_data.GetHeight()
412
413
414
415
416 rescaled_width = (float(current_width) / current_height) * rescaled_height
417 img_data.Rescale(rescaled_width, rescaled_height, quality = wx.IMAGE_QUALITY_HIGH)
418 bitmap = wx.Bitmap(img_data)
419 del img_data
420 except Exception:
421 _log.exception('cannot load image from [%s]', filename)
422 del img_data
423 del bitmap
424 return None
425 return bitmap
426
427
428 -def gm_show_error(aMessage=None, aTitle = None, error=None, title=None):
429
430 if error is None:
431 error = aMessage
432 if error is None:
433 error = _('programmer forgot to specify error message')
434 error += _("\n\nPlease consult the error log for all the gory details !")
435
436 if title is None:
437 title = aTitle
438 if title is None:
439 title = _('generic error message')
440
441 dlg = wx.MessageDialog (
442 parent = None,
443 message = error,
444 caption = title,
445 style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP
446 )
447 dlg.ShowModal()
448 dlg.Destroy()
449 return True
450
451
452 -def gm_show_info(aMessage=None, aTitle=None, info=None, title=None):
453
454 if info is None:
455 info = aMessage
456 if info is None:
457 info = _('programmer forgot to specify info message')
458
459 if title is None:
460 title = aTitle
461 if title is None:
462 title = _('generic info message')
463
464 dlg = wx.MessageDialog (
465 parent = None,
466 message = info,
467 caption = title,
468 style = wx.OK | wx.ICON_INFORMATION | wx.STAY_ON_TOP
469 )
470 dlg.ShowModal()
471 dlg.Destroy()
472 return True
473
474
476 if aMessage is None:
477 aMessage = _('programmer forgot to specify warning')
478
479 if aTitle is None:
480 aTitle = _('generic warning message')
481
482 dlg = wx.MessageDialog (
483 parent = None,
484 message = aMessage,
485 caption = aTitle,
486 style = wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP
487 )
488 dlg.ShowModal()
489 dlg.Destroy()
490 return True
491
492
493 -def gm_show_question(aMessage='programmer forgot to specify question', aTitle='generic user question dialog', cancel_button=False, question=None, title=None):
494 if cancel_button:
495 style = wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION | wx.STAY_ON_TOP
496 else:
497 style = wx.YES_NO | wx.ICON_QUESTION | wx.STAY_ON_TOP
498
499 if question is None:
500 question = aMessage
501 if title is None:
502 title = aTitle
503
504 dlg = wx.MessageDialog(None, question, title, style)
505 btn_pressed = dlg.ShowModal()
506 dlg.Destroy()
507
508 if btn_pressed == wx.ID_YES:
509 return True
510 elif btn_pressed == wx.ID_NO:
511 return False
512 else:
513 return None
514
515
516 if __name__ == '__main__':
517
518 if len(sys.argv) < 2:
519 sys.exit()
520
521 if sys.argv[1] != 'test':
522 sys.exit()
523
524 from Gnumed.pycommon import gmI18N
525 gmI18N.activate_locale()
526 gmI18N.install_domain(domain='gnumed')
527
528
530 app = wx.App()
531 img = file2scaled_image(filename = sys.argv[2])
532 print(img)
533 print(img.Height)
534 print(img.Width)
535
537 app = wx.PyWidgetTester(size = (200, 50))
538 prw = cThreeValuedLogicPhraseWheel(app.frame, -1)
539 app.frame.Show(True)
540 app.MainLoop()
541
542 return True
543
545 app = wx.PyWidgetTester(size = (200, 50))
546 result = clipboard2file()
547 if result is False:
548 print("problem opening clipboard")
549 return
550 if result is None:
551 print("no data in clipboard")
552 return
553 print("file:", result)
554
555
556
557 test_clipboard()
558
559
560