| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed date input widget
2
3 All GNUmed date input should happen via classes in
4 this module. Initially this is just a plain text box
5 but using this throughout GNUmed will allow us to
6 transparently add features.
7
8 @copyright: author(s)
9 """
10 #==============================================================================
11 __version__ = "$Revision: 1.66 $"
12 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
13 __licence__ = "GPL (details at http://www.gnu.org)"
14
15 # standard libary
16 import re, string, sys, time, datetime as pyDT, logging
17
18
19 # 3rd party
20 import mx.DateTime as mxDT
21 import wx
22 import wx.calendar
23
24
25 # GNUmed specific
26 if __name__ == '__main__':
27 sys.path.insert(0, '../../')
28 from Gnumed.pycommon import gmMatchProvider, gmDateTime
29 from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers
30
31 _log = logging.getLogger('gm.ui')
32
33 #============================================================
35
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 #============================================================
85
86 gmMatchProvider.cMatchProvider.__init__(self)
87
88 self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999)
89 self.word_separators = None
90 # self.ignored_chars("""[?!."'\\(){}\[\]<>~#*$%^_]+""")
91 #--------------------------------------------------------
92 # external API
93 #--------------------------------------------------------
94 #--------------------------------------------------------
95 # base class API
96 #--------------------------------------------------------
97 # internal matching algorithms
98 #
99 # if we end up here:
100 # - aFragment will not be "None"
101 # - aFragment will be lower case
102 # - we _do_ deliver matches (whether we find any is a different story)
103 #--------------------------------------------------------
105 """Return matches for aFragment at start of phrases."""
106 matches = gmDateTime.str2pydt_matches(str2parse = aFragment.strip())
107 if len(matches) > 0:
108 return (True, matches)
109 else:
110 return (False, [])
111 #--------------------------------------------------------
113 """Return matches for aFragment at start of words inside phrases."""
114 return self.getMatchesByPhrase(aFragment)
115 #--------------------------------------------------------
117 """Return matches for aFragment as a true substring."""
118 return self.getMatchesByPhrase(aFragment)
119 #--------------------------------------------------------
121 """Return all items."""
122
123 matches = (False, [])
124 return matches
125
126 dlg = cCalendarDatePickerDlg(None)
127 # FIXME: show below parent
128 dlg.CentreOnScreen()
129
130 if dlg.ShowModal() == wx.ID_OK:
131 date = dlg.cal.Date
132 if date is not None:
133 if date.IsValid():
134 date = gmDateTime.wxDate2py_dt(wxDate = date)
135 matches = (True, [{'data': date, 'label': date.strftime('%Y-%m-%d')}])
136 dlg.Destroy()
137
138 return matches
139 #============================================================
141
143
144 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
145
146 self.matcher = cDateMatchProvider()
147 self.phrase_separators = None
148 self.selection_only = True
149 self.selection_only_error_msg = _('Cannot interpret input as timestamp.')
150 #--------------------------------------------------------
151 # internal helpers
152 #--------------------------------------------------------
154
155 if val is None:
156 val = self.GetValue().strip()
157
158 success, matches = self.matcher.getMatchesByPhrase(val)
159
160 if len(matches) == 1:
161 return matches[0]['data']
162
163 return None
164 #--------------------------------------------------------
166 dlg = cCalendarDatePickerDlg(self)
167 # FIXME: show below parent
168 dlg.CentreOnScreen()
169 decision = dlg.ShowModal()
170 date = dlg.cal.Date
171 dlg.Destroy()
172
173 if decision != wx.ID_OK:
174 return
175
176 if date is None:
177 return
178
179 if not date.IsValid():
180 return
181
182 date = gmDateTime.wxDate2py_dt(wxDate = date)
183 self.SetText(value = date.strftime('%Y-%m-%d'), data = date, suppress_smarts = True)
184 #--------------------------------------------------------
185 # phrasewheel internal API
186 #--------------------------------------------------------
188 # are we valid ?
189 if self.data is None:
190 # no, so try
191 self.data = self.__text2timestamp()
192
193 # let the base class do its thing
194 super(self.__class__, self)._on_lose_focus(event)
195 #--------------------------------------------------------
197 data = self._picklist.GetSelectedItemData()
198 if data is not None:
199 return data.strftime('%Y-%m-%d')
200 return self._picklist.get_selected_item_label()
201 #--------------------------------------------------------
203
204 if event.AltDown() is False:
205 keycode = event.GetKeyCode()
206 if keycode == wx.WXK_F4:
207 self.__pick_from_calendar()
208 return
209
210 if self.GetValue().strip() != u'':
211 super(self.__class__, self)._on_key_down(event)
212 #--------------------------------------------------------
213 # external API
214 #--------------------------------------------------------
216
217 if isinstance(value, pyDT.datetime):
218 self.SetText(data = value, suppress_smarts = True)
219 return
220
221 if value is None:
222 value = u''
223
224 super(self.__class__, self).SetValue(value)
225 #--------------------------------------------------------
227
228 if data is not None:
229 if isinstance(data, gmDateTime.cFuzzyTimestamp):
230 data = data.timestamp
231 if value.strip() == u'':
232 value = data.strftime('%Y-%m-%d')
233
234 super(self.__class__, self).SetText(value = value, data = data, suppress_smarts = suppress_smarts)
235 #--------------------------------------------------------
237 if data is None:
238 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None)
239 else:
240 if isinstance(data, gmDateTime.cFuzzyTimestamp):
241 data = data.timestamp
242 super(self.__class__, self).SetText(value = data.strftime('%Y-%m-%d'), data = data)
243 #--------------------------------------------------------
257
258 #============================================================
261 self.__allow_past = 1
262 self.__shifting_base = None
263
264 gmMatchProvider.cMatchProvider.__init__(self)
265
266 self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999)
267 self.word_separators = None
268 # self.ignored_chars("""[?!."'\\(){}\[\]<>~#*$%^_]+""")
269 #--------------------------------------------------------
270 # external API
271 #--------------------------------------------------------
272 #--------------------------------------------------------
273 # base class API
274 #--------------------------------------------------------
275 # internal matching algorithms
276 #
277 # if we end up here:
278 # - aFragment will not be "None"
279 # - aFragment will be lower case
280 # - we _do_ deliver matches (whether we find any is a different story)
281 #--------------------------------------------------------
283 """Return matches for aFragment at start of phrases."""
284 self.__now = mxDT.now()
285 matches = gmDateTime.str2fuzzy_timestamp_matches(aFragment.strip())
286 if len(matches) > 0:
287 return (True, matches)
288 else:
289 return (False, [])
290 #--------------------------------------------------------
292 """Return matches for aFragment at start of words inside phrases."""
293 return self.getMatchesByPhrase(aFragment)
294 #--------------------------------------------------------
296 """Return matches for aFragment as a true substring."""
297 return self.getMatchesByPhrase(aFragment)
298 #--------------------------------------------------------
303 #==================================================
305
307
308 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
309
310 self.matcher = cMatchProvider_FuzzyTimestamp()
311 self.phrase_separators = None
312 self.selection_only = True
313 self.selection_only_error_msg = _('Cannot interpret input as timestamp.')
314 #--------------------------------------------------------
315 # internal helpers
316 #--------------------------------------------------------
318
319 if val is None:
320 val = self.GetValue().strip()
321
322 success, matches = self.matcher.getMatchesByPhrase(val)
323 if len(matches) == 1:
324 return matches[0]['data']
325
326 return None
327 #--------------------------------------------------------
328 # phrasewheel internal API
329 #--------------------------------------------------------
331 # are we valid ?
332 if self.data is None:
333 # no, so try
334 self.data = self.__text2timestamp()
335
336 # let the base class do its thing
337 gmPhraseWheel.cPhraseWheel._on_lose_focus(self, event)
338 #--------------------------------------------------------
340 data = self._picklist.GetSelectedItemData()
341 if data is not None:
342 return data.format_accurately()
343 return self._picklist.get_selected_item_label()
344 #--------------------------------------------------------
345 # external API
346 #--------------------------------------------------------
348
349 if data is not None:
350 if isinstance(data, pyDT.datetime):
351 data = gmDateTime.cFuzzyTimestamp(timestamp=data)
352 if value.strip() == u'':
353 value = data.format_accurately()
354
355 gmPhraseWheel.cPhraseWheel.SetText(self, value = value, data = data, suppress_smarts = suppress_smarts)
356 #--------------------------------------------------------
358 if data is None:
359 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None)
360 else:
361 if isinstance(data, pyDT.datetime):
362 data = gmDateTime.cFuzzyTimestamp(timestamp=data)
363 gmPhraseWheel.cPhraseWheel.SetText(self, value = data.format_accurately(), data = data)
364 #--------------------------------------------------------
378 #==================================================
390 #==================================================
392
393 #----------------------------------------------
395 """Set either datetime.datetime or wx.DateTime"""
396
397 if isinstance(value, (pyDT.date, pyDT.datetime)):
398 value = gmDateTime.py_dt2wxDate(py_dt = value, wx = wx)
399
400 elif value is None:
401 value = wx.DefaultDateTime
402
403 wx.DatePickerCtrl.SetValue(self, value)
404 #----------------------------------------------
406 """Returns datetime.datetime values"""
407
408 # datepicker can fail to pick up user changes by keyboard until
409 # it has lost focus, so do that but also set the focus back to
410 # us, now this is a side-effect (after GetValue() focus will be
411 # here) but at least it is predictable ...
412 self.Navigate()
413 self.SetFocus()
414 value = wx.DatePickerCtrl.GetValue(self)
415
416 if value is None:
417 return None
418
419 # manage null dates (useful when wx.DP_ALLOWNONE is set)
420 if not value.IsValid():
421 if invalid_as_none:
422 return None
423 else:
424 return value
425
426 self.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
427 self.Refresh()
428
429 if not as_pydt:
430 return value
431
432 return gmDateTime.wxDate2py_dt(value)
433 #----------------------------------------------
434 # def convenience wrapper
435 #----------------------------------------------
437 val = self.GetValue(as_pydt = False, invalid_as_none = invalid_as_none)
438
439 if val is None:
440 if allow_none:
441 valid = True
442 else:
443 valid = False
444 else:
445 valid = val.IsValid()
446
447 if valid:
448 self.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
449 else:
450 self.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
451
452 self.Refresh()
453 return valid
454 #----------------------------------------------
456 return self.GetValue(as_pydt = True)
457 #----------------------------------------------
459 if valid is True:
460 self.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
461 else:
462 self.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
463 self.Refresh()
464 #==================================================
465 # main
466 #--------------------------------------------------
467 if __name__ == '__main__':
468
469 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
470 from Gnumed.pycommon import gmI18N
471 gmI18N.activate_locale()
472 gmI18N.install_domain(domain='gnumed')
473 gmDateTime.init()
474
475 #----------------------------------------------------
477 mp = cMatchProvider_FuzzyTimestamp()
478 mp.word_separators = None
479 mp.setThresholds(aWord = 998, aSubstring = 999)
480 val = None
481 while val != 'exit':
482 print "************************************"
483 val = raw_input('Enter date fragment: ')
484 found, matches = mp.getMatches(aFragment=val)
485 for match in matches:
486 print match['label']
487 print match['data']
488 print "---------------"
489 #--------------------------------------------------------
491 app = wx.PyWidgetTester(size = (200, 300))
492 app.SetWidget(cFuzzyTimestampInput, id=-1, size=(180,20), pos=(10,20))
493 app.MainLoop()
494 #--------------------------------------------------------
496 app = wx.PyWidgetTester(size = (200, 300))
497 app.SetWidget(cDateInputCtrl, id=-1, size=(180,20), pos=(10,20))
498 app.MainLoop()
499 #--------------------------------------------------------
500 #test_cli()
501 #test_gui()
502 test_picker()
503
504 #==================================================
505 # - free text input: start string with "
506 #==================================================
507
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Jan 10 03:56:48 2011 | http://epydoc.sourceforge.net |