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
288 """TreeCtrl mixin class to record expansion history."""
289 - def __init__(self):
290 if not isinstance(self, wx.TreeCtrl):
291 raise TypeError('[%s]: mixin can only be applied to wx.TreeCtrl, not [%s]' % (cTreeExpansionHistoryMixin, self.__class__.__name__))
292 self.expansion_state = {}
293
294
295
297 self.__record_subtree_expansion(start_node_id = self.GetRootItem())
298
300 if len(self.expansion_state) == 0:
301 return True
302 self.__restore_subtree_expansion(start_node_id = self.GetRootItem())
303
304 - def print_expansion(self):
305 if len(self.expansion_state) == 0:
306 print "currently no expansion snapshot available"
307 return True
308 print "last snapshot of state of expansion"
309 print "-----------------------------------"
310 print "listing expanded nodes:"
311 for node_id in self.expansion_state.keys():
312 print "node ID:", node_id
313 print " selected:", self.expansion_state[node_id]
314
315
316
317 - def __record_subtree_expansion(self, start_node_id=None):
318 """This records node expansion states based on the item label.
319
320 A side effect of this is that identically named items can
321 become unduly synchronized in their expand state after a
322 snapshot/restore cycle.
323
324 Better choices might be
325
326 id(item.GetPyData()) or
327 item.GetPyData().get_tree_uid()
328
329 where get_tree_uid():
330
331 '[%s:%s]' % (self.__class__.__name__, id(self))
332
333 or some such. This would survive renaming of the item.
334
335 For database items it may be useful to include the
336 primary key which would - contrary to id() - survive
337 reloads from the database.
338 """
339
340
341 if not start_node_id.IsOk():
342 return True
343
344 if not self.IsExpanded(start_node_id):
345 return True
346
347 self.expansion_state[self.GetItemText(start_node_id)] = self.IsSelected(start_node_id)
348
349 child_id, cookie = self.GetFirstChild(start_node_id)
350 while child_id.IsOk():
351 self.__record_subtree_expansion(start_node_id = child_id)
352 child_id, cookie = self.GetNextChild(start_node_id, cookie)
353
354 return
355
356 - def __restore_subtree_expansion(self, start_node_id=None):
357 start_node_label = self.GetItemText(start_node_id)
358 try:
359 node_selected = self.expansion_state[start_node_label]
360 except KeyError:
361 return
362
363 self.Expand(start_node_id)
364 if node_selected:
365 self.SelectItem(start_node_id)
366
367 child_id, cookie = self.GetFirstChild(start_node_id)
368 while child_id.IsOk():
369 self.__restore_subtree_expansion(start_node_id = child_id)
370 child_id, cookie = self.GetNextChild(start_node_id, cookie)
371
372 return
373
375 """Generic file drop target class.
376
377 Protocol:
378 Widgets being declared file drop targets
379 must provide the method:
380
381 add_filenames(filenames)
382 """
383
385 wx.FileDropTarget.__init__(self)
386 self.target = target
387 _log.debug('setting up [%s] as file drop target', target)
388
391
393 img_data = None
394 bitmap = None
395 rescaled_height = height
396 try:
397 img_data = wx.Image(filename, wx.BITMAP_TYPE_ANY)
398 current_width = img_data.GetWidth()
399 current_height = img_data.GetHeight()
400
401
402
403
404 rescaled_width = (float(current_width) / current_height) * rescaled_height
405 img_data.Rescale(rescaled_width, rescaled_height, quality = wx.IMAGE_QUALITY_HIGH)
406 bitmap = wx.BitmapFromImage(img_data)
407 del img_data
408 except StandardError:
409 _log.exception('cannot load image from [%s]', filename)
410 del img_data
411 del bitmap
412 return None
413
414 return bitmap
415
416 -def gm_show_error(aMessage=None, aTitle = None, error=None, title=None):
417
418 if error is None:
419 error = aMessage
420 if error is None:
421 error = _('programmer forgot to specify error message')
422 error += _("\n\nPlease consult the error log for all the gory details !")
423
424 if title is None:
425 title = aTitle
426 if title is None:
427 title = _('generic error message')
428
429 dlg = wx.MessageDialog (
430 parent = None,
431 message = error,
432 caption = title,
433 style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP
434 )
435 dlg.ShowModal()
436 dlg.Destroy()
437 return True
438
439 -def gm_show_info(aMessage=None, aTitle=None, info=None, title=None):
440
441 if info is None:
442 info = aMessage
443 if info is None:
444 info = _('programmer forgot to specify info message')
445
446 if title is None:
447 title = aTitle
448 if title is None:
449 title = _('generic info message')
450
451 dlg = wx.MessageDialog (
452 parent = None,
453 message = info,
454 caption = title,
455 style = wx.OK | wx.ICON_INFORMATION | wx.STAY_ON_TOP
456 )
457 dlg.ShowModal()
458 dlg.Destroy()
459 return True
460
462 if aMessage is None:
463 aMessage = _('programmer forgot to specify warning')
464
465 if aTitle is None:
466 aTitle = _('generic warning message')
467
468 dlg = wx.MessageDialog (
469 parent = None,
470 message = aMessage,
471 caption = aTitle,
472 style = wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP
473 )
474 dlg.ShowModal()
475 dlg.Destroy()
476 return True
477
478 -def gm_show_question(aMessage='programmer forgot to specify question', aTitle='generic user question dialog', cancel_button=False, question=None, title=None):
479 if cancel_button:
480 style = wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION | wx.STAY_ON_TOP
481 else:
482 style = wx.YES_NO | wx.ICON_QUESTION | wx.STAY_ON_TOP
483
484 if question is None:
485 question = aMessage
486 if title is None:
487 title = aTitle
488
489 dlg = wx.MessageDialog(None, question, title, style)
490 btn_pressed = dlg.ShowModal()
491 dlg.Destroy()
492
493 if btn_pressed == wx.ID_YES:
494 return True
495 elif btn_pressed == wx.ID_NO:
496 return False
497 else:
498 return None
499
500
501 if __name__ == '__main__':
502
503 if len(sys.argv) < 2:
504 sys.exit()
505
506 if sys.argv[1] != 'test':
507 sys.exit()
508
509 from Gnumed.pycommon import gmI18N
510 gmI18N.activate_locale()
511 gmI18N.install_domain(domain='gnumed')
512
513
515 app = wx.App()
516 img = file2scaled_image(filename = sys.argv[2])
517 print img
518 print img.Height
519 print img.Width
520
522 app = wx.PyWidgetTester(size = (200, 50))
523 prw = cThreeValuedLogicPhraseWheel(parent = app.frame, id = -1)
524 app.frame.Show(True)
525 app.MainLoop()
526
527 return True
528
529
530 test_sql_logic_prw()
531
532
533