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 This source code is protected by the GPL licensing scheme.
8 Details regarding the GPL are available at http://www.gnu.org
9 You may use and share it as long as you don't deny this right
10 to anybody else.
11 """
12
13 __version__ = "$Revision: 1.106 $"
14 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
15 __license__ = "GPL (details at http://www.gnu.org)"
16
17 import os, logging
18
19
20 import wx
21
22
23 from Gnumed.pycommon import gmPG2
24 from Gnumed.pycommon import gmTools
25 from Gnumed.pycommon import gmI18N
26
27
28 _log = logging.getLogger('gm.main')
29
30 from Gnumed.wxGladeWidgets import wxg2ButtonQuestionDlg
31
103
104 from Gnumed.wxGladeWidgets import wxg3ButtonQuestionDlg
105
149
150 from Gnumed.wxGladeWidgets import wxgMultilineTextEntryDlg
151
152 -class cMultilineTextEntryDlg(wxgMultilineTextEntryDlg.wxgMultilineTextEntryDlg):
153 """Editor for a bit of text."""
154
155 - def __init__(self, *args, **kwargs):
156
157 try:
158 title = kwargs['title']
159 del kwargs['title']
160 except KeyError:
161 title = None
162
163 try:
164 msg = kwargs['msg']
165 del kwargs['msg']
166 except KeyError:
167 msg = None
168
169 try:
170 data = kwargs['data']
171 del kwargs['data']
172 except KeyError:
173 data = None
174
175 try:
176 self.original_text = kwargs['text']
177 del kwargs['text']
178 except KeyError:
179 self.original_text = None
180
181 wxgMultilineTextEntryDlg.wxgMultilineTextEntryDlg.__init__(self, *args, **kwargs)
182
183 if title is not None:
184 self.SetTitle(title)
185
186 if self.original_text is not None:
187 self._TCTRL_text.SetValue(self.original_text)
188 self._BTN_restore.Enable(True)
189
190 if msg is None:
191 self._LBL_msg.Hide()
192 else:
193 self._LBL_msg.SetLabel(msg)
194 self.Layout()
195 self.Refresh()
196
197 if data is None:
198 self._TCTRL_data.Hide()
199 else:
200 self._TCTRL_data.SetValue(data)
201 self.Layout()
202 self.Refresh()
203
204 - def _get_value(self):
205 return self._TCTRL_text.GetValue()
206
207 value = property(_get_value, lambda x:x)
208
209
210
212
213 if self.IsModal():
214 self.EndModal(wx.ID_SAVE)
215 else:
216 self.Close()
217
220
222 if self.original_text is not None:
223 self._TCTRL_text.SetValue(self.original_text)
224
225 from Gnumed.business import gmSurgery
226 from Gnumed.wxGladeWidgets import wxgGreetingEditorDlg
227
244
246 """TreeCtrl mixin class to record expansion history."""
247 - def __init__(self):
248 if not isinstance(self, wx.TreeCtrl):
249 raise TypeError('[%s]: mixin can only be applied to wx.TreeCtrl, not [%s]' % (cTreeExpansionHistoryMixin, self.__class__.__name__))
250 self.expansion_state = {}
251
252
253
255 self.__record_subtree_expansion(start_node_id = self.GetRootItem())
256
258 if len(self.expansion_state) == 0:
259 return True
260 self.__restore_subtree_expansion(start_node_id = self.GetRootItem())
261
262 - def print_expansion(self):
263 if len(self.expansion_state) == 0:
264 print "currently no expansion snapshot available"
265 return True
266 print "last snapshot of state of expansion"
267 print "-----------------------------------"
268 print "listing expanded nodes:"
269 for node_id in self.expansion_state.keys():
270 print "node ID:", node_id
271 print " selected:", self.expansion_state[node_id]
272
273
274
275 - def __record_subtree_expansion(self, start_node_id=None):
276 """This records node expansion states based on the item label.
277
278 A side effect of this is that identically named items can
279 become unduly synchronized in their expand state after a
280 snapshot/restore cycle.
281
282 Better choices might be
283
284 id(item.GetPyData()) or
285 item.GetPyData().get_tree_uid()
286
287 where get_tree_uid():
288
289 '[%s:%s]' % (self.__class__.__name__, id(self))
290
291 or some such. This would survive renaming of the item.
292
293 For database items it may be useful to include the
294 primary key which would - contrary to id() - survive
295 reloads from the database.
296 """
297
298
299 if not start_node_id.IsOk():
300 return True
301
302 if not self.IsExpanded(start_node_id):
303 return True
304
305 self.expansion_state[self.GetItemText(start_node_id)] = self.IsSelected(start_node_id)
306
307 child_id, cookie = self.GetFirstChild(start_node_id)
308 while child_id.IsOk():
309 self.__record_subtree_expansion(start_node_id = child_id)
310 child_id, cookie = self.GetNextChild(start_node_id, cookie)
311
312 return
313
314 - def __restore_subtree_expansion(self, start_node_id=None):
315 start_node_label = self.GetItemText(start_node_id)
316 try:
317 node_selected = self.expansion_state[start_node_label]
318 except KeyError:
319 return
320
321 self.Expand(start_node_id)
322 if node_selected:
323 self.SelectItem(start_node_id)
324
325 child_id, cookie = self.GetFirstChild(start_node_id)
326 while child_id.IsOk():
327 self.__restore_subtree_expansion(start_node_id = child_id)
328 child_id, cookie = self.GetNextChild(start_node_id, cookie)
329
330 return
331
333 """Generic file drop target class.
334
335 Protocol:
336 Widgets being declared file drop targets
337 must provide the method:
338
339 add_filenames(filenames)
340 """
341
343 wx.FileDropTarget.__init__(self)
344 self.target = target
345
348
350 img_data = None
351 bitmap = None
352 rescaled_height = height
353 try:
354 img_data = wx.Image(filename, wx.BITMAP_TYPE_ANY)
355 current_width = img_data.GetWidth()
356 current_height = img_data.GetHeight()
357 rescaled_width = (current_width / current_height) * rescaled_height
358 img_data.Rescale(rescaled_width, rescaled_height, quality = wx.IMAGE_QUALITY_HIGH)
359 bitmap = wx.BitmapFromImage(img_data)
360 del img_data
361 except StandardError:
362 _log.exception('cannot load visual progress note from [%s]', filename)
363 del img_data
364 del bitmap
365 return None
366
367 return bitmap
368
370 if aMessage is None:
371 aMessage = _('programmer forgot to specify error message')
372
373 aMessage += _("\n\nPlease consult the error log for all the gory details !")
374
375 if aTitle is None:
376 aTitle = _('generic error message')
377
378 dlg = wx.MessageDialog (
379 parent = None,
380 message = aMessage,
381 caption = aTitle,
382 style = wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP
383 )
384 dlg.ShowModal()
385 dlg.Destroy()
386 return True
387
389 if aMessage is None:
390 aMessage = _('programmer forgot to specify info message')
391
392 if aTitle is None:
393 aTitle = _('generic info message')
394
395 dlg = wx.MessageDialog (
396 parent = None,
397 message = aMessage,
398 caption = aTitle,
399 style = wx.OK | wx.ICON_INFORMATION | wx.STAY_ON_TOP
400 )
401 dlg.ShowModal()
402 dlg.Destroy()
403 return True
404
406 if aMessage is None:
407 aMessage = _('programmer forgot to specify warning')
408
409 if aTitle is None:
410 aTitle = _('generic warning message')
411
412 dlg = wx.MessageDialog (
413 parent = None,
414 message = aMessage,
415 caption = aTitle,
416 style = wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP
417 )
418 dlg.ShowModal()
419 dlg.Destroy()
420 return True
421
422 -def gm_show_question(aMessage='programmer forgot to specify question', aTitle='generic user question dialog', cancel_button=False):
423 if cancel_button:
424 style = wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION | wx.STAY_ON_TOP
425 else:
426 style = wx.YES_NO | wx.ICON_QUESTION | wx.STAY_ON_TOP
427
428 dlg = wx.MessageDialog (
429 None,
430 aMessage,
431 aTitle,
432 style
433 )
434 btn_pressed = dlg.ShowModal()
435 dlg.Destroy()
436
437 if btn_pressed == wx.ID_YES:
438 return True
439 elif btn_pressed == wx.ID_NO:
440 return False
441 else:
442 return None
443
444