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