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
15
16 import wx
17
18
19 if __name__ == '__main__':
20 sys.path.insert(0, '../../')
21 from Gnumed.pycommon import gmMatchProvider
22 from Gnumed.pycommon import gmExceptions
23 from Gnumed.pycommon import gmLog2
24 from Gnumed.pycommon import gmTools
25 from Gnumed.wxpython import gmPhraseWheel
26
27
28 _log = logging.getLogger('gm.main')
29
31
33
34 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
35
36 items = [
37 {'list_label': _('Yes: + / ! / 1'), 'field_label': _('yes'), 'data': True, 'weight': 0},
38 {'list_label': _('No: - / 0'), 'field_label': _('no'), 'data': False, 'weight': 1},
39 {'list_label': _('Unknown: ?'), 'field_label': _('unknown'), 'data': None, 'weight': 2},
40 ]
41 mp = gmMatchProvider.cMatchProvider_FixedList(items)
42 mp.setThresholds(1, 1, 2)
43 mp.word_separators = '[ :/]+'
44 mp.word_separators = None
45 mp.ignored_chars = r"[.'\\(){}\[\]<>~#*$%^_=&@\t23456]+" + r'"'
46
47 self.matcher = mp
48
49 from Gnumed.wxGladeWidgets import wxg2ButtonQuestionDlg
50
122
123 from Gnumed.wxGladeWidgets import wxg3ButtonQuestionDlg
124
196
197 from Gnumed.wxGladeWidgets import wxgMultilineTextEntryDlg
198
199 -class cMultilineTextEntryDlg(wxgMultilineTextEntryDlg.wxgMultilineTextEntryDlg):
200 """Editor for a bit of text."""
201
202 - def __init__(self, *args, **kwargs):
203
204 try:
205 title = kwargs['title']
206 del kwargs['title']
207 except KeyError:
208 title = None
209
210 try:
211 msg = kwargs['msg']
212 del kwargs['msg']
213 except KeyError:
214 msg = None
215
216 try:
217 data = kwargs['data']
218 del kwargs['data']
219 except KeyError:
220 data = None
221
222 try:
223 self.original_text = kwargs['text']
224 del kwargs['text']
225 except KeyError:
226 self.original_text = None
227
228 wxgMultilineTextEntryDlg.wxgMultilineTextEntryDlg.__init__(self, *args, **kwargs)
229
230 if title is not None:
231 self.SetTitle(title)
232
233 if self.original_text is not None:
234 self._TCTRL_text.SetValue(self.original_text)
235 self._BTN_restore.Enable(True)
236
237 if msg is None:
238 self._LBL_msg.Hide()
239 else:
240 self._LBL_msg.SetLabel(msg)
241 self.Layout()
242 self.Refresh()
243
244 if data is None:
245 self._TCTRL_data.Hide()
246 else:
247 self._TCTRL_data.SetValue(data)
248 self.Layout()
249 self.Refresh()
250
251 self._TCTRL_text.SetFocus()
252
253
254
255 - def _get_value(self):
256 return self._TCTRL_text.GetValue()
257
258 value = property(_get_value, lambda x:x)
259
261 return self._CHBOX_is_already_formatted.IsChecked()
262
263 is_user_formatted = property(_get_is_user_formatted, lambda x:x)
264
266 self._CHBOX_is_already_formatted.Enable(value)
267
268 enable_user_formatting = property(lambda x:x, _set_enable_user_formatting)
269
270
271
273
274 if self.IsModal():
275 self.EndModal(wx.ID_SAVE)
276 else:
277 self.Close()
278
281
283 if self.original_text is not None:
284 self._TCTRL_text.SetValue(self.original_text)
285
286 from Gnumed.business import gmSurgery
287 from Gnumed.wxGladeWidgets import wxgGreetingEditorDlg
288
305
307 """TreeCtrl mixin class to record expansion history."""
308 - def __init__(self):
309 if not isinstance(self, wx.TreeCtrl):
310 raise TypeError('[%s]: mixin can only be applied to wx.TreeCtrl, not [%s]' % (cTreeExpansionHistoryMixin, self.__class__.__name__))
311 self.expansion_state = {}
312
313
314
316 self.__record_subtree_expansion(start_node_id = self.GetRootItem())
317
319 if len(self.expansion_state) == 0:
320 return True
321 self.__restore_subtree_expansion(start_node_id = self.GetRootItem())
322
323 - def print_expansion(self):
324 if len(self.expansion_state) == 0:
325 print "currently no expansion snapshot available"
326 return True
327 print "last snapshot of state of expansion"
328 print "-----------------------------------"
329 print "listing expanded nodes:"
330 for node_id in self.expansion_state.keys():
331 print "node ID:", node_id
332 print " selected:", self.expansion_state[node_id]
333
334
335
336 - def __record_subtree_expansion(self, start_node_id=None):
337 """This records node expansion states based on the item label.
338
339 A side effect of this is that identically named items can
340 become unduly synchronized in their expand state after a
341 snapshot/restore cycle.
342
343 Better choices might be
344
345 id(item.GetPyData()) or
346 item.GetPyData().get_tree_uid()
347
348 where get_tree_uid():
349
350 '[%s:%s]' % (self.__class__.__name__, id(self))
351
352 or some such. This would survive renaming of the item.
353
354 For database items it may be useful to include the
355 primary key which would - contrary to id() - survive
356 reloads from the database.
357 """
358
359
360 if not start_node_id.IsOk():
361 return True
362
363 if not self.IsExpanded(start_node_id):
364 return True
365
366 self.expansion_state[self.GetItemText(start_node_id)] = self.IsSelected(start_node_id)
367
368 child_id, cookie = self.GetFirstChild(start_node_id)
369 while child_id.IsOk():
370 self.__record_subtree_expansion(start_node_id = child_id)
371 child_id, cookie = self.GetNextChild(start_node_id, cookie)
372
373 return
374
375 - def __restore_subtree_expansion(self, start_node_id=None):
376 start_node_label = self.GetItemText(start_node_id)
377 try:
378 node_selected = self.expansion_state[start_node_label]
379 except KeyError:
380 return
381
382 self.Expand(start_node_id)
383 if node_selected:
384 self.SelectItem(start_node_id)
385
386 child_id, cookie = self.GetFirstChild(start_node_id)
387 while child_id.IsOk():
388 self.__restore_subtree_expansion(start_node_id = child_id)
389 child_id, cookie = self.GetNextChild(start_node_id, cookie)
390
391 return
392
394 """Generic file drop target class.
395
396 Protocol:
397 Widgets being declared file drop targets
398 must provide the method:
399
400 add_filenames(filenames)
401 """
402
404 wx.FileDropTarget.__init__(self)
405 self.target = target
406 _log.debug('setting up [%s] as file drop target', target)
407
410
412 img_data = None
413 bitmap = None
414 rescaled_height = height
415 try:
416 img_data = wx.Image(filename, wx.BITMAP_TYPE_ANY)
417 current_width = img_data.GetWidth()
418 current_height = img_data.GetHeight()
419
420
421
422
423 rescaled_width = (float(current_width) / current_height) * rescaled_height
424 img_data.Rescale(rescaled_width, rescaled_height, quality = wx.IMAGE_QUALITY_HIGH)
425 bitmap = wx.BitmapFromImage(img_data)
426 del img_data
427 except StandardError:
428 _log.exception('cannot load image from [%s]', filename)
429 del img_data
430 del bitmap
431 return None
432
433 return bitmap
434
436 if aMessage is None:
437 aMessage = _('programmer forgot to specify error message')
438
439 aMessage += _("\n\nPlease consult the error log for all the gory details !")
440
441 if aTitle is None:
442 aTitle = _('generic error message')
443
444 dlg = wx.MessageDialog (
445 parent = None,
446 message = aMessage,
447 caption = aTitle,
448 style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP
449 )
450 dlg.ShowModal()
451 dlg.Destroy()
452 return True
453
455 if aMessage is None:
456 aMessage = _('programmer forgot to specify info message')
457
458 if aTitle is None:
459 aTitle = _('generic info message')
460
461 dlg = wx.MessageDialog (
462 parent = None,
463 message = aMessage,
464 caption = aTitle,
465 style = wx.OK | wx.ICON_INFORMATION | wx.STAY_ON_TOP
466 )
467 dlg.ShowModal()
468 dlg.Destroy()
469 return True
470
472 if aMessage is None:
473 aMessage = _('programmer forgot to specify warning')
474
475 if aTitle is None:
476 aTitle = _('generic warning message')
477
478 dlg = wx.MessageDialog (
479 parent = None,
480 message = aMessage,
481 caption = aTitle,
482 style = wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP
483 )
484 dlg.ShowModal()
485 dlg.Destroy()
486 return True
487
488 -def gm_show_question(aMessage='programmer forgot to specify question', aTitle='generic user question dialog', cancel_button=False, question=None, title=None):
489 if cancel_button:
490 style = wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION | wx.STAY_ON_TOP
491 else:
492 style = wx.YES_NO | wx.ICON_QUESTION | wx.STAY_ON_TOP
493
494 if question is None:
495 question = aMessage
496 if title is None:
497 title = aTitle
498
499 dlg = wx.MessageDialog(None, question, title, style)
500 btn_pressed = dlg.ShowModal()
501 dlg.Destroy()
502
503 if btn_pressed == wx.ID_YES:
504 return True
505 elif btn_pressed == wx.ID_NO:
506 return False
507 else:
508 return None
509
510
511 if __name__ == '__main__':
512
513 if len(sys.argv) < 2:
514 sys.exit()
515
516 if sys.argv[1] != 'test':
517 sys.exit()
518
519 from Gnumed.pycommon import gmI18N
520 gmI18N.activate_locale()
521 gmI18N.install_domain(domain='gnumed')
522
523
525 app = wx.App()
526 img = file2scaled_image(filename = sys.argv[2])
527 print img
528 print img.Height
529 print img.Width
530
532 app = wx.PyWidgetTester(size = (200, 50))
533 prw = cThreeValuedLogicPhraseWheel(parent = app.frame, id = -1)
534 app.frame.Show(True)
535 app.MainLoop()
536
537 return True
538
539
540 test_sql_logic_prw()
541
542
543