| 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 v2 or later (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
30 from Gnumed.wxpython import gmGuiHelpers
31
32 _log = logging.getLogger('gm.ui')
33
34 #============================================================
35 #class cIntervalMatchProvider(gmMatchProvider.cMatchProvider):
36 # """Turns strings into candidate intervals."""
37 # def __init__(self):
38 #
39 # gmMatchProvider.cMatchProvider.__init__(self)
40 #
41 # self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999)
42 # self.word_separators = None
43 ## self.ignored_chars("""[?!."'\\(){}\[\]<>~#*$%^_]+""")
44 # #--------------------------------------------------------
45 # # external API
46 # #--------------------------------------------------------
47 # #--------------------------------------------------------
48 # # base class API
49 # #--------------------------------------------------------
50 # def getMatchesByPhrase(self, aFragment):
51 # intv = gmDateTime.str2interval(str_interval = aFragment)
52 #
53 # if intv is None:
54 # return (False, [])
55 #
56 # items = [{
57 # 'data': intv,
58 # 'field_label': gmDateTime.format_interval(intv, gmDateTime.acc_minutes),
59 # 'list_label': gmDateTime.format_interval(intv, gmDateTime.acc_minutes)
60 # }]
61 #
62 # return (True, items)
63 # #--------------------------------------------------------
64 # def getMatchesByWord(self, aFragment):
65 # return self.getMatchesByPhrase(aFragment)
66 # #--------------------------------------------------------
67 # def getMatchesBySubstr(self, aFragment):
68 # return self.getMatchesByPhrase(aFragment)
69 # #--------------------------------------------------------
70 # def getAllMatches(self):
71 # matches = (False, [])
72 # return matches
73 #============================================================
75
77
78 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
79 self.phrase_separators = None
80 self.display_accuracy = None
81 #--------------------------------------------------------
82 # phrasewheel internal API
83 #--------------------------------------------------------
85 intv = gmDateTime.str2interval(str_interval = val)
86 if intv is None:
87 self._current_match_candidates = []
88 else:
89 self._current_match_candidates = [{
90 'data': intv,
91 'field_label': gmDateTime.format_interval(intv, gmDateTime.acc_minutes),
92 'list_label': gmDateTime.format_interval(intv, gmDateTime.acc_minutes)
93 }]
94 self._picklist.SetItems(self._current_match_candidates)
95 #---------------------------------------------------------
96 # def _on_lose_focus(self, event):
97 # # are we valid ?
98 # if len(self._data) == 0:
99 # self._set_data_to_first_match()
100 #
101 # # let the base class do its thing
102 # super(cIntervalPhraseWheel, self)._on_lose_focus(event)
103 #--------------------------------------------------------
105 intv = item['data']
106 if intv is not None:
107 return gmDateTime.format_interval (
108 interval = intv,
109 accuracy_wanted = self.display_accuracy
110 )
111 return item['field_label']
112 #--------------------------------------------------------
114 intv = self.GetData()
115 print intv
116 if intv is None:
117 return u''
118 return gmDateTime.format_interval (
119 interval = intv,
120 accuracy_wanted = self.display_accuracy
121 )
122 #--------------------------------------------------------
123 # external API
124 #--------------------------------------------------------
126
127 if isinstance(value, pyDT.timedelta):
128 self.SetText(data = value, suppress_smarts = True)
129 return
130
131 if value is None:
132 value = u''
133
134 super(cIntervalPhraseWheel, self).SetValue(value)
135 #--------------------------------------------------------
137
138 if data is not None:
139 if value.strip() == u'':
140 value = gmDateTime.format_interval (
141 interval = data,
142 accuracy_wanted = self.display_accuracy
143 )
144
145 super(cIntervalPhraseWheel, self).SetText(value = value, data = data, suppress_smarts = suppress_smarts)
146 #--------------------------------------------------------
148 if data is None:
149 super(cIntervalPhraseWheel, self).SetText(u'', None)
150 return
151
152 value = gmDateTime.format_interval (
153 interval = data,
154 accuracy_wanted = self.display_accuracy
155 )
156 super(cIntervalPhraseWheel, self).SetText(value = value, data = data)
157 #--------------------------------------------------------
159 if len(self._data) == 0:
160 self._set_data_to_first_match()
161
162 return super(cIntervalPhraseWheel, self).GetData()
163 #============================================================
165 """Shows a calendar control from which the user can pick a date."""
167
168 wx.Dialog.__init__(self, parent, title = _('Pick a date ...'))
169 panel = wx.Panel(self, -1)
170
171 sizer = wx.BoxSizer(wx.VERTICAL)
172 panel.SetSizer(sizer)
173
174 cal = wx.calendar.CalendarCtrl(panel)
175
176 if sys.platform != 'win32':
177 # gtk truncates the year - this fixes it
178 w, h = cal.Size
179 cal.Size = (w+25, h)
180 cal.MinSize = cal.Size
181
182 sizer.Add(cal, 0)
183
184 button_sizer = wx.BoxSizer(wx.HORIZONTAL)
185 button_sizer.Add((0, 0), 1)
186 btn_ok = wx.Button(panel, wx.ID_OK)
187 btn_ok.SetDefault()
188 button_sizer.Add(btn_ok, 0, wx.ALL, 2)
189 button_sizer.Add((0, 0), 1)
190 btn_can = wx.Button(panel, wx.ID_CANCEL)
191 button_sizer.Add(btn_can, 0, wx.ALL, 2)
192 button_sizer.Add((0, 0), 1)
193 sizer.Add(button_sizer, 1, wx.EXPAND | wx.ALL, 10)
194 sizer.Fit(panel)
195 self.ClientSize = panel.Size
196
197 cal.Bind(wx.EVT_KEY_DOWN, self.__on_key_down)
198 cal.SetFocus()
199 self.cal = cal
200 #-----------------------------------------------------------
211
212 #============================================================
214 """Turns strings into candidate dates.
215
216 Matching on "all" (*, '') will pop up a calendar :-)
217 """
219
220 gmMatchProvider.cMatchProvider.__init__(self)
221
222 self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999)
223 self.word_separators = None
224 # self.ignored_chars("""[?!."'\\(){}\[\]<>~#*$%^_]+""")
225 #--------------------------------------------------------
226 # external API
227 #--------------------------------------------------------
228 #--------------------------------------------------------
229 # base class API
230 #--------------------------------------------------------
231 # internal matching algorithms
232 #
233 # if we end up here:
234 # - aFragment will not be "None"
235 # - aFragment will be lower case
236 # - we _do_ deliver matches (whether we find any is a different story)
237 #--------------------------------------------------------
239 """Return matches for aFragment at start of phrases."""
240 matches = gmDateTime.str2pydt_matches(str2parse = aFragment.strip())
241
242 if len(matches) == 0:
243 return (False, [])
244
245 items = []
246 for match in matches:
247 if match['data'] is None:
248 list_label = match['label']
249 data = None
250 else:
251 data = match['data'].replace (
252 hour = 11,
253 minute = 11,
254 second = 11,
255 microsecond = 111111
256 )
257 list_label = gmDateTime.pydt_strftime (
258 data,
259 format = '%A, %d. %B %Y (%x)',
260 accuracy = gmDateTime.acc_days
261 )
262 items.append ({
263 'data': data,
264 'field_label': match['label'],
265 'list_label': list_label
266 })
267
268 return (True, items)
269 #--------------------------------------------------------
271 """Return matches for aFragment at start of words inside phrases."""
272 return self.getMatchesByPhrase(aFragment)
273 #--------------------------------------------------------
275 """Return matches for aFragment as a true substring."""
276 return self.getMatchesByPhrase(aFragment)
277 #--------------------------------------------------------
283
284 # # consider this:
285 # dlg = cCalendarDatePickerDlg(None)
286 # # FIXME: show below parent
287 # dlg.CentreOnScreen()
288 #
289 # if dlg.ShowModal() == wx.ID_OK:
290 # date = dlg.cal.Date
291 # if date is not None:
292 # if date.IsValid():
293 # date = gmDateTime.wxDate2py_dt(wxDate = date).replace (
294 # hour = 11,
295 # minute = 11,
296 # second = 11,
297 # microsecond = 111111
298 # )
299 # lbl = gmDateTime.pydt_strftime(date, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)
300 # matches = (True, [{'data': date, 'label': lbl}])
301 # dlg.Destroy()
302 #
303 # return matches
304 #============================================================
306
308
309 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
310
311 self.matcher = cDateMatchProvider()
312 self.phrase_separators = None
313
314 self.static_tooltip_extra = _('<ALT-C/K>: pick from (c/k)alendar')
315 #--------------------------------------------------------
316 # internal helpers
317 #--------------------------------------------------------
318 # def __text2timestamp(self):
319 #
320 # self._update_candidates_in_picklist(val = self.GetValue().strip())
321 #
322 # if len(self._current_match_candidates) == 1:
323 # return self._current_match_candidates[0]['data']
324 #
325 # return None
326 #--------------------------------------------------------
328 dlg = cCalendarDatePickerDlg(self)
329 # FIXME: show below parent
330 dlg.CentreOnScreen()
331 decision = dlg.ShowModal()
332 date = dlg.cal.Date
333 dlg.Destroy()
334
335 if decision != wx.ID_OK:
336 return
337
338 if date is None:
339 return
340
341 if not date.IsValid():
342 return
343
344 date = gmDateTime.wxDate2py_dt(wxDate = date).replace (
345 hour = 11,
346 minute = 11,
347 second = 11,
348 microsecond = 111111
349 )
350 val = gmDateTime.pydt_strftime(date, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)
351 self.SetText(value = val, data = date, suppress_smarts = True)
352 #--------------------------------------------------------
353 # phrasewheel internal API
354 #--------------------------------------------------------
356 # are we valid ?
357 if len(self._data) == 0:
358 self._set_data_to_first_match()
359
360 # let the base class do its thing
361 super(cDateInputPhraseWheel, self)._on_lose_focus(event)
362 #--------------------------------------------------------
364 data = item['data']
365 if data is not None:
366 return gmDateTime.pydt_strftime(data, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)
367 return item['field_label']
368 #--------------------------------------------------------
370
371 # <ALT-C> / <ALT-K> -> calendar
372 if event.AltDown() is True:
373 char = unichr(event.GetUnicodeKey())
374 if char in u'ckCK':
375 self.__pick_from_calendar()
376 return
377
378 super(cDateInputPhraseWheel, self)._on_key_down(event)
379 #--------------------------------------------------------
381 if len(self._data) == 0:
382 return u''
383
384 date = self.GetData()
385 # if match provider only provided completions
386 # but not a full date with it
387 if date is None:
388 return u''
389
390 return gmDateTime.pydt_strftime (
391 date,
392 format = '%A, %d. %B %Y (%x)',
393 accuracy = gmDateTime.acc_days
394 )
395 #--------------------------------------------------------
396 # external API
397 #--------------------------------------------------------
399
400 if isinstance(value, pyDT.datetime):
401 date = value.replace (
402 hour = 11,
403 minute = 11,
404 second = 11,
405 microsecond = 111111
406 )
407 self.SetText(data = date, suppress_smarts = True)
408 return
409
410 if value is None:
411 value = u''
412
413 super(self.__class__, self).SetValue(value)
414 #--------------------------------------------------------
416
417 if data is not None:
418 if isinstance(data, gmDateTime.cFuzzyTimestamp):
419 data = data.timestamp.replace (
420 hour = 11,
421 minute = 11,
422 second = 11,
423 microsecond = 111111
424 )
425 if value.strip() == u'':
426 value = gmDateTime.pydt_strftime(data, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)
427
428 super(self.__class__, self).SetText(value = value, data = data, suppress_smarts = suppress_smarts)
429 #--------------------------------------------------------
431 if data is None:
432 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None)
433 else:
434 if isinstance(data, gmDateTime.cFuzzyTimestamp):
435 data = data.timestamp.replace (
436 hour = 11,
437 minute = 11,
438 second = 11,
439 microsecond = 111111
440 )
441 val = gmDateTime.pydt_strftime(data, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)
442 super(self.__class__, self).SetText(value = val, data = data)
443 #--------------------------------------------------------
445 if len(self._data) == 0:
446 self._set_data_to_first_match()
447
448 return super(self.__class__, self).GetData()
449 #--------------------------------------------------------
451 if len(self._data) > 0:
452 self.display_as_valid(True)
453 return True
454
455 if self.GetValue().strip() == u'':
456 if allow_empty:
457 self.display_as_valid(True)
458 return True
459 else:
460 self.display_as_valid(False)
461 return False
462
463 # skip showing calendar on '*' from here
464 if self.GetValue().strip() == u'*':
465 self.display_as_valid(False)
466 return False
467
468 self._set_data_to_first_match()
469 if len(self._data) == 0:
470 self.display_as_valid(False)
471 return False
472
473 self.display_as_valid(True)
474 return True
475 #--------------------------------------------------------
476 # properties
477 #--------------------------------------------------------
479 return self.GetData()
480
483 # val = gmDateTime.pydt_strftime(date, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)
484 # self.data = date.replace (
485 # hour = 11,
486 # minute = 11,
487 # second = 11,
488 # microsecond = 111111
489 # )
490
491 date = property(_get_date, _set_date)
492 #============================================================
495 self.__allow_past = 1
496 self.__shifting_base = None
497
498 gmMatchProvider.cMatchProvider.__init__(self)
499
500 self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999)
501 self.word_separators = None
502 # self.ignored_chars("""[?!."'\\(){}\[\]<>~#*$%^_]+""")
503 #--------------------------------------------------------
504 # external API
505 #--------------------------------------------------------
506 #--------------------------------------------------------
507 # base class API
508 #--------------------------------------------------------
509 # internal matching algorithms
510 #
511 # if we end up here:
512 # - aFragment will not be "None"
513 # - aFragment will be lower case
514 # - we _do_ deliver matches (whether we find any is a different story)
515 #--------------------------------------------------------
517 """Return matches for aFragment at start of phrases."""
518 # self.__now = mxDT.now()
519 matches = gmDateTime.str2fuzzy_timestamp_matches(aFragment.strip())
520
521 if len(matches) == 0:
522 return (False, [])
523
524 items = []
525 for match in matches:
526 # if match['data'] is None:
527 # list_label = match['label']
528 # else:
529 # list_label = gmDateTime.pydt_strftime (
530 # match['data'].timestamp.format_accurately(),
531 # format = '%A, %d. %B %Y (%x)',
532 # accuracy = gmDateTime.acc_days
533 # )
534 items.append ({
535 'data': match['data'],
536 'field_label': match['label'],
537 'list_label': match['label']
538 })
539
540 return (True, items)
541 #--------------------------------------------------------
543 """Return matches for aFragment at start of words inside phrases."""
544 return self.getMatchesByPhrase(aFragment)
545 #--------------------------------------------------------
547 """Return matches for aFragment as a true substring."""
548 return self.getMatchesByPhrase(aFragment)
549 #--------------------------------------------------------
553 #==================================================
555
557
558 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
559
560 self.matcher = cMatchProvider_FuzzyTimestamp()
561 self.phrase_separators = None
562 self.selection_only = True
563 self.selection_only_error_msg = _('Cannot interpret input as timestamp.')
564 #--------------------------------------------------------
565 # internal helpers
566 #--------------------------------------------------------
568
569 if val is None:
570 val = self.GetValue().strip()
571
572 success, matches = self.matcher.getMatchesByPhrase(val)
573 if len(matches) == 1:
574 return matches[0]['data']
575
576 return None
577 #--------------------------------------------------------
578 # phrasewheel internal API
579 #--------------------------------------------------------
581 # are we valid ?
582 if self.data is None:
583 # no, so try
584 self.data = self.__text2timestamp()
585
586 # let the base class do its thing
587 gmPhraseWheel.cPhraseWheel._on_lose_focus(self, event)
588 #--------------------------------------------------------
590 data = item['data']
591 if data is not None:
592 return data.format_accurately()
593 return item['field_label']
594 #--------------------------------------------------------
595 # external API
596 #--------------------------------------------------------
598
599 if data is not None:
600 if isinstance(data, pyDT.datetime):
601 data = gmDateTime.cFuzzyTimestamp(timestamp=data)
602 if value.strip() == u'':
603 value = data.format_accurately()
604
605 gmPhraseWheel.cPhraseWheel.SetText(self, value = value, data = data, suppress_smarts = suppress_smarts)
606 #--------------------------------------------------------
608 if data is None:
609 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None)
610 else:
611 if isinstance(data, pyDT.datetime):
612 data = gmDateTime.cFuzzyTimestamp(timestamp=data)
613 gmPhraseWheel.cPhraseWheel.SetText(self, value = data.format_accurately(), data = data)
614 #--------------------------------------------------------
628 #==================================================
629 # main
630 #--------------------------------------------------
631 if __name__ == '__main__':
632
633 if len(sys.argv) < 2:
634 sys.exit()
635
636 if sys.argv[1] != 'test':
637 sys.exit()
638
639 gmI18N.activate_locale()
640 gmI18N.install_domain(domain='gnumed')
641 gmDateTime.init()
642
643 #----------------------------------------------------
645 mp = cMatchProvider_FuzzyTimestamp()
646 mp.word_separators = None
647 mp.setThresholds(aWord = 998, aSubstring = 999)
648 val = None
649 while val != 'exit':
650 print "************************************"
651 val = raw_input('Enter date fragment ("exit" to quit): ')
652 found, matches = mp.getMatches(aFragment=val)
653 for match in matches:
654 #print match
655 print match['label']
656 print match['data']
657 print "---------------"
658 #--------------------------------------------------------
660 app = wx.PyWidgetTester(size = (300, 40))
661 app.SetWidget(cFuzzyTimestampInput, id=-1, size=(180,20), pos=(10,20))
662 app.MainLoop()
663 #--------------------------------------------------------
665 app = wx.PyWidgetTester(size = (300, 40))
666 app.SetWidget(cDateInputPhraseWheel, id=-1, size=(180,20), pos=(10,20))
667 app.MainLoop()
668 #--------------------------------------------------------
669 #test_cli()
670 test_fuzzy_picker()
671 #test_picker()
672
673 #==================================================
674
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu Jul 28 03:57:26 2011 | http://epydoc.sourceforge.net |