| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed date input widget
2
3 All GNUmed date input should happen via classes in
4 this module.
5
6 @copyright: author(s)
7 """
8 #==============================================================================
9 __version__ = "$Revision: 1.66 $"
10 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
11 __licence__ = "GPL (details at http://www.gnu.org)"
12
13 # standard libary
14 import re, string, sys, time, datetime as pyDT, logging
15
16
17 # 3rd party
18 import mx.DateTime as mxDT
19 import wx
20 import wx.calendar
21
22
23 # GNUmed specific
24 if __name__ == '__main__':
25 sys.path.insert(0, '../../')
26 from Gnumed.pycommon import gmMatchProvider
27 from Gnumed.pycommon import gmDateTime
28 from Gnumed.pycommon import gmI18N
29 from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers
30
31 _log = logging.getLogger('gm.ui')
32
33 #============================================================
35 """Shows a calendar control from which the user can pick a date."""
37
38 wx.Dialog.__init__(self, parent, title = _('Pick a date ...'))
39 panel = wx.Panel(self, -1)
40
41 sizer = wx.BoxSizer(wx.VERTICAL)
42 panel.SetSizer(sizer)
43
44 cal = wx.calendar.CalendarCtrl(panel)
45
46 if sys.platform != 'win32':
47 # gtk truncates the year - this fixes it
48 w, h = cal.Size
49 cal.Size = (w+25, h)
50 cal.MinSize = cal.Size
51
52 sizer.Add(cal, 0)
53
54 button_sizer = wx.BoxSizer(wx.HORIZONTAL)
55 button_sizer.Add((0, 0), 1)
56 btn_ok = wx.Button(panel, wx.ID_OK)
57 btn_ok.SetDefault()
58 button_sizer.Add(btn_ok, 0, wx.ALL, 2)
59 button_sizer.Add((0, 0), 1)
60 btn_can = wx.Button(panel, wx.ID_CANCEL)
61 button_sizer.Add(btn_can, 0, wx.ALL, 2)
62 button_sizer.Add((0, 0), 1)
63 sizer.Add(button_sizer, 1, wx.EXPAND | wx.ALL, 10)
64 sizer.Fit(panel)
65 self.ClientSize = panel.Size
66
67 cal.Bind(wx.EVT_KEY_DOWN, self.__on_key_down)
68 cal.SetFocus()
69 self.cal = cal
70 #-----------------------------------------------------------
81
82 #============================================================
84 """Turns strings into candidate dates.
85
86 Matching on "all" (*, '') will pop up a calendar :-)
87 """
89
90 gmMatchProvider.cMatchProvider.__init__(self)
91
92 self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999)
93 self.word_separators = None
94 # self.ignored_chars("""[?!."'\\(){}\[\]<>~#*$%^_]+""")
95 #--------------------------------------------------------
96 # external API
97 #--------------------------------------------------------
98 #--------------------------------------------------------
99 # base class API
100 #--------------------------------------------------------
101 # internal matching algorithms
102 #
103 # if we end up here:
104 # - aFragment will not be "None"
105 # - aFragment will be lower case
106 # - we _do_ deliver matches (whether we find any is a different story)
107 #--------------------------------------------------------
109 """Return matches for aFragment at start of phrases."""
110 matches = gmDateTime.str2pydt_matches(str2parse = aFragment.strip())
111 if len(matches) > 0:
112 return (True, matches)
113 else:
114 return (False, [])
115 #--------------------------------------------------------
117 """Return matches for aFragment at start of words inside phrases."""
118 return self.getMatchesByPhrase(aFragment)
119 #--------------------------------------------------------
121 """Return matches for aFragment as a true substring."""
122 return self.getMatchesByPhrase(aFragment)
123 #--------------------------------------------------------
125 """Return all items."""
126
127 matches = (False, [])
128 return matches
129
130 dlg = cCalendarDatePickerDlg(None)
131 # FIXME: show below parent
132 dlg.CentreOnScreen()
133
134 if dlg.ShowModal() == wx.ID_OK:
135 date = dlg.cal.Date
136 if date is not None:
137 if date.IsValid():
138 date = gmDateTime.wxDate2py_dt(wxDate = date)
139 matches = (True, [{'data': date, 'label': date.strftime('%Y-%m-%d')}])
140 dlg.Destroy()
141
142 return matches
143 #============================================================
145
147
148 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
149
150 self.matcher = cDateMatchProvider()
151 self.phrase_separators = None
152
153 self.static_tooltip_extra = _('<ALT-C>: pick from calendar')
154 #--------------------------------------------------------
155 # internal helpers
156 #--------------------------------------------------------
158
159 if val is None:
160 val = self.GetValue().strip()
161
162 success, matches = self.matcher.getMatchesByPhrase(val)
163
164 if len(matches) == 1:
165 return matches[0]['data']
166
167 return None
168 #--------------------------------------------------------
170 dlg = cCalendarDatePickerDlg(self)
171 # FIXME: show below parent
172 dlg.CentreOnScreen()
173 decision = dlg.ShowModal()
174 date = dlg.cal.Date
175 dlg.Destroy()
176
177 if decision != wx.ID_OK:
178 return
179
180 if date is None:
181 return
182
183 if not date.IsValid():
184 return
185
186 date = gmDateTime.wxDate2py_dt(wxDate = date)
187 self.SetText(value = date.strftime('%Y-%m-%d'), data = date, suppress_smarts = True)
188 #--------------------------------------------------------
189 # phrasewheel internal API
190 #--------------------------------------------------------
192 # are we valid ?
193 if self.data is None:
194 # no, so try
195 self.data = self.__text2timestamp()
196
197 # let the base class do its thing
198 super(self.__class__, self)._on_lose_focus(event)
199 #--------------------------------------------------------
201 data = self._picklist.GetSelectedItemData()
202 if data is not None:
203 return data.strftime('%Y-%m-%d')
204 return self._picklist.get_selected_item_label()
205 #--------------------------------------------------------
207
208 # <ALT-C> / <ALT-K> -> calendar
209 if event.AltDown() is True:
210 char = unichr(event.GetUnicodeKey())
211 if char in u'ckCK':
212 self.__pick_from_calendar()
213 return
214
215 super(self.__class__, self)._on_key_down(event)
216 #--------------------------------------------------------
218 if self.data is None:
219 return u''
220
221 return self.data.strftime('%A, %d. %B %Y (%x)').decode(gmI18N.get_encoding())
222 #--------------------------------------------------------
223 # external API
224 #--------------------------------------------------------
226
227 if isinstance(value, pyDT.datetime):
228 self.SetText(data = value, suppress_smarts = True)
229 return
230
231 if value is None:
232 value = u''
233
234 super(self.__class__, self).SetValue(value)
235 #--------------------------------------------------------
237
238 if data is not None:
239 if isinstance(data, gmDateTime.cFuzzyTimestamp):
240 data = data.timestamp
241 if value.strip() == u'':
242 value = data.strftime('%Y-%m-%d')
243
244 super(self.__class__, self).SetText(value = value, data = data, suppress_smarts = suppress_smarts)
245 #--------------------------------------------------------
247 if data is None:
248 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None)
249 else:
250 if isinstance(data, gmDateTime.cFuzzyTimestamp):
251 data = data.timestamp
252 super(self.__class__, self).SetText(value = data.strftime('%Y-%m-%d'), data = data)
253 #--------------------------------------------------------
255 if self.data is None:
256 self.data = self.__text2timestamp()
257
258 return super(self.__class__, self).GetData()
259 #--------------------------------------------------------
261 if self.data is not None:
262 self.display_as_valid(True)
263 return True
264
265 if self.GetValue().strip() == u'':
266 if allow_empty:
267 self.display_as_valid(True)
268 return True
269 else:
270 self.display_as_valid(False)
271 return False
272
273 # skip showing calendar on '*' from here
274 if self.GetValue().strip() == u'*':
275 self.display_as_valid(False)
276 return False
277
278 self.data = self.__text2timestamp()
279 if self.data is None:
280 self.display_as_valid(False)
281 return False
282
283 self.display_as_valid(True)
284 return True
285 #--------------------------------------------------------
286 # properties
287 #--------------------------------------------------------
289 return self.data
290
293
294 date = property(_get_date, _set_date)
295 #============================================================
298 self.__allow_past = 1
299 self.__shifting_base = None
300
301 gmMatchProvider.cMatchProvider.__init__(self)
302
303 self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999)
304 self.word_separators = None
305 # self.ignored_chars("""[?!."'\\(){}\[\]<>~#*$%^_]+""")
306 #--------------------------------------------------------
307 # external API
308 #--------------------------------------------------------
309 #--------------------------------------------------------
310 # base class API
311 #--------------------------------------------------------
312 # internal matching algorithms
313 #
314 # if we end up here:
315 # - aFragment will not be "None"
316 # - aFragment will be lower case
317 # - we _do_ deliver matches (whether we find any is a different story)
318 #--------------------------------------------------------
320 """Return matches for aFragment at start of phrases."""
321 self.__now = mxDT.now()
322 matches = gmDateTime.str2fuzzy_timestamp_matches(aFragment.strip())
323 if len(matches) > 0:
324 return (True, matches)
325 else:
326 return (False, [])
327 #--------------------------------------------------------
329 """Return matches for aFragment at start of words inside phrases."""
330 return self.getMatchesByPhrase(aFragment)
331 #--------------------------------------------------------
333 """Return matches for aFragment as a true substring."""
334 return self.getMatchesByPhrase(aFragment)
335 #--------------------------------------------------------
340 #==================================================
342
344
345 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
346
347 self.matcher = cMatchProvider_FuzzyTimestamp()
348 self.phrase_separators = None
349 self.selection_only = True
350 self.selection_only_error_msg = _('Cannot interpret input as timestamp.')
351 #--------------------------------------------------------
352 # internal helpers
353 #--------------------------------------------------------
355
356 if val is None:
357 val = self.GetValue().strip()
358
359 success, matches = self.matcher.getMatchesByPhrase(val)
360 if len(matches) == 1:
361 return matches[0]['data']
362
363 return None
364 #--------------------------------------------------------
365 # phrasewheel internal API
366 #--------------------------------------------------------
368 # are we valid ?
369 if self.data is None:
370 # no, so try
371 self.data = self.__text2timestamp()
372
373 # let the base class do its thing
374 gmPhraseWheel.cPhraseWheel._on_lose_focus(self, event)
375 #--------------------------------------------------------
377 data = self._picklist.GetSelectedItemData()
378 if data is not None:
379 return data.format_accurately()
380 return self._picklist.get_selected_item_label()
381 #--------------------------------------------------------
382 # external API
383 #--------------------------------------------------------
385
386 if data is not None:
387 if isinstance(data, pyDT.datetime):
388 data = gmDateTime.cFuzzyTimestamp(timestamp=data)
389 if value.strip() == u'':
390 value = data.format_accurately()
391
392 gmPhraseWheel.cPhraseWheel.SetText(self, value = value, data = data, suppress_smarts = suppress_smarts)
393 #--------------------------------------------------------
395 if data is None:
396 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None)
397 else:
398 if isinstance(data, pyDT.datetime):
399 data = gmDateTime.cFuzzyTimestamp(timestamp=data)
400 gmPhraseWheel.cPhraseWheel.SetText(self, value = data.format_accurately(), data = data)
401 #--------------------------------------------------------
415 #==================================================
427 #==================================================
428 #class cDateInputCtrl(wx.DatePickerCtrl):
429 #
430 # #----------------------------------------------
431 # def SetValue(self, value):
432 # """Set either datetime.datetime or wx.DateTime"""
433 #
434 # if isinstance(value, (pyDT.date, pyDT.datetime)):
435 # value = gmDateTime.py_dt2wxDate(py_dt = value, wx = wx)
436 #
437 # elif value is None:
438 # value = wx.DefaultDateTime
439 #
440 # wx.DatePickerCtrl.SetValue(self, value)
441 # #----------------------------------------------
442 # def GetValue(self, as_pydt=False, invalid_as_none=False):
443 # """Returns datetime.datetime values"""
444 #
445 # # datepicker can fail to pick up user changes by keyboard until
446 # # it has lost focus, so do that but also set the focus back to us,
447 # # now, this is a side-effect (after .GetValue focus will be
448 # # here) but at least it is predictable ...
449 # self.Navigate()
450 # self.SetFocus()
451 # value = wx.DatePickerCtrl.GetValue(self)
452 #
453 # if value is None:
454 # return None
455 #
456 # # manage null dates (useful when wx.DP_ALLOWNONE is set)
457 # if not value.IsValid():
458 # if invalid_as_none:
459 # return None
460 # else:
461 # return value
462 #
463 # self.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
464 # self.Refresh()
465 #
466 # if not as_pydt:
467 # return value
468 #
469 # return gmDateTime.wxDate2py_dt(value)
470 # #----------------------------------------------
471 # # def convenience wrapper
472 # #----------------------------------------------
473 # def is_valid_timestamp(self, allow_none=True, invalid_as_none=False):
474 # val = self.GetValue(as_pydt = False, invalid_as_none = invalid_as_none)
475 #
476 # if val is None:
477 # if allow_none:
478 # valid = True
479 # else:
480 # valid = False
481 # else:
482 # valid = val.IsValid()
483 #
484 # if valid:
485 # self.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
486 # else:
487 # self.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
488 #
489 # self.Refresh()
490 # return valid
491 # #----------------------------------------------
492 # def get_pydt(self):
493 # return self.GetValue(as_pydt = True)
494 # #----------------------------------------------
495 # def display_as_valid(self, valid=True):
496 # if valid is True:
497 # self.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
498 # else:
499 # self.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
500 # self.Refresh()
501 #==================================================
502 # main
503 #--------------------------------------------------
504 if __name__ == '__main__':
505
506 if len(sys.argv) < 2:
507 sys.exit()
508
509 if sys.argv[2] != 'test':
510 sys.exit()
511
512 gmI18N.activate_locale()
513 gmI18N.install_domain(domain='gnumed')
514 gmDateTime.init()
515
516 #----------------------------------------------------
518 mp = cMatchProvider_FuzzyTimestamp()
519 mp.word_separators = None
520 mp.setThresholds(aWord = 998, aSubstring = 999)
521 val = None
522 while val != 'exit':
523 print "************************************"
524 val = raw_input('Enter date fragment: ')
525 found, matches = mp.getMatches(aFragment=val)
526 for match in matches:
527 print match['label']
528 print match['data']
529 print "---------------"
530 #--------------------------------------------------------
532 app = wx.PyWidgetTester(size = (200, 300))
533 app.SetWidget(cFuzzyTimestampInput, id=-1, size=(180,20), pos=(10,20))
534 app.MainLoop()
535 # #--------------------------------------------------------
536 # def test_picker():
537 # app = wx.PyWidgetTester(size = (200, 300))
538 # app.SetWidget(cDateInputCtrl, id=-1, size=(180,20), pos=(10,20))
539 # app.MainLoop()
540 #--------------------------------------------------------
541 #test_cli()
542 #test_gui()
543 test_picker()
544
545 #==================================================
546
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Apr 12 03:58:56 2011 | http://epydoc.sourceforge.net |