| 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
113 if len(matches) == 0:
114 return (False, [])
115
116 items = []
117 for match in matches:
118 if match['data'] is None:
119 list_label = match['label']
120 else:
121 list_label = gmDateTime.pydt_strftime (
122 match['data'],
123 format = '%A, %d. %B %Y (%x)',
124 accuracy = gmDateTime.acc_days
125 )
126 items.append ({
127 'data': match['data'],
128 'field_label': match['label'],
129 'list_label': list_label
130 })
131
132 return (True, items)
133 #--------------------------------------------------------
135 """Return matches for aFragment at start of words inside phrases."""
136 return self.getMatchesByPhrase(aFragment)
137 #--------------------------------------------------------
139 """Return matches for aFragment as a true substring."""
140 return self.getMatchesByPhrase(aFragment)
141 #--------------------------------------------------------
147
148 # # consider this:
149 # dlg = cCalendarDatePickerDlg(None)
150 # # FIXME: show below parent
151 # dlg.CentreOnScreen()
152 #
153 # if dlg.ShowModal() == wx.ID_OK:
154 # date = dlg.cal.Date
155 # if date is not None:
156 # if date.IsValid():
157 # date = gmDateTime.wxDate2py_dt(wxDate = date)
158 # lbl = gmDateTime.pydt_strftime(date, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)
159 # matches = (True, [{'data': date, 'label': lbl}])
160 # dlg.Destroy()
161 #
162 # return matches
163 #============================================================
165
167
168 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
169
170 self.matcher = cDateMatchProvider()
171 self.phrase_separators = None
172
173 self.static_tooltip_extra = _('<ALT-C/K>: pick from (c/k)alendar')
174 #--------------------------------------------------------
175 # internal helpers
176 #--------------------------------------------------------
177 # def __text2timestamp(self):
178 #
179 # self._update_candidates_in_picklist(val = self.GetValue().strip())
180 #
181 # if len(self._current_match_candidates) == 1:
182 # return self._current_match_candidates[0]['data']
183 #
184 # return None
185 #--------------------------------------------------------
187 dlg = cCalendarDatePickerDlg(self)
188 # FIXME: show below parent
189 dlg.CentreOnScreen()
190 decision = dlg.ShowModal()
191 date = dlg.cal.Date
192 dlg.Destroy()
193
194 if decision != wx.ID_OK:
195 return
196
197 if date is None:
198 return
199
200 if not date.IsValid():
201 return
202
203 date = gmDateTime.wxDate2py_dt(wxDate = date)
204 val = gmDateTime.pydt_strftime(date, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)
205 self.SetText(value = val, data = date, suppress_smarts = True)
206 #--------------------------------------------------------
207 # phrasewheel internal API
208 #--------------------------------------------------------
210 # are we valid ?
211 if len(self._data) == 0:
212 self._set_data_to_first_match()
213
214 # let the base class do its thing
215 super(cDateInputPhraseWheel, self)._on_lose_focus(event)
216 #--------------------------------------------------------
218 data = item['data']
219 if data is not None:
220 return gmDateTime.pydt_strftime(data, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)
221 return item['field_label']
222 #--------------------------------------------------------
224
225 # <ALT-C> / <ALT-K> -> calendar
226 if event.AltDown() is True:
227 char = unichr(event.GetUnicodeKey())
228 if char in u'ckCK':
229 self.__pick_from_calendar()
230 return
231
232 super(cDateInputPhraseWheel, self)._on_key_down(event)
233 #--------------------------------------------------------
235 if len(self._data) == 0:
236 return u''
237
238 date = self.GetData()
239 # if match provider only provided completions
240 # but not a full date with it
241 if date is None:
242 return u''
243
244 return gmDateTime.pydt_strftime (
245 date,
246 format = '%A, %d. %B %Y (%x)',
247 accuracy = gmDateTime.acc_days
248 )
249 #--------------------------------------------------------
250 # external API
251 #--------------------------------------------------------
253
254 if isinstance(value, pyDT.datetime):
255 self.SetText(data = value, suppress_smarts = True)
256 return
257
258 if value is None:
259 value = u''
260
261 super(self.__class__, self).SetValue(value)
262 #--------------------------------------------------------
264
265 if data is not None:
266 if isinstance(data, gmDateTime.cFuzzyTimestamp):
267 data = data.timestamp
268 if value.strip() == u'':
269 value = gmDateTime.pydt_strftime(data, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)
270
271 super(self.__class__, self).SetText(value = value, data = data, suppress_smarts = suppress_smarts)
272 #--------------------------------------------------------
274 if data is None:
275 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None)
276 else:
277 if isinstance(data, gmDateTime.cFuzzyTimestamp):
278 data = data.timestamp
279 val = gmDateTime.pydt_strftime(data, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)
280 super(self.__class__, self).SetText(value = val, data = data)
281 #--------------------------------------------------------
283 if len(self._data) == 0:
284 self._set_data_to_first_match()
285
286 return super(self.__class__, self).GetData()
287 #--------------------------------------------------------
289 if len(self._data) > 0:
290 self.display_as_valid(True)
291 return True
292
293 if self.GetValue().strip() == u'':
294 if allow_empty:
295 self.display_as_valid(True)
296 return True
297 else:
298 self.display_as_valid(False)
299 return False
300
301 # skip showing calendar on '*' from here
302 if self.GetValue().strip() == u'*':
303 self.display_as_valid(False)
304 return False
305
306 self._set_data_to_first_match()
307 if len(self._data) == 0:
308 self.display_as_valid(False)
309 return False
310
311 self.display_as_valid(True)
312 return True
313 #--------------------------------------------------------
314 # properties
315 #--------------------------------------------------------
317 return self.GetData()
318
321 # val = gmDateTime.pydt_strftime(date, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)
322 # self.data = date
323
324 date = property(_get_date, _set_date)
325 #============================================================
328 self.__allow_past = 1
329 self.__shifting_base = None
330
331 gmMatchProvider.cMatchProvider.__init__(self)
332
333 self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999)
334 self.word_separators = None
335 # self.ignored_chars("""[?!."'\\(){}\[\]<>~#*$%^_]+""")
336 #--------------------------------------------------------
337 # external API
338 #--------------------------------------------------------
339 #--------------------------------------------------------
340 # base class API
341 #--------------------------------------------------------
342 # internal matching algorithms
343 #
344 # if we end up here:
345 # - aFragment will not be "None"
346 # - aFragment will be lower case
347 # - we _do_ deliver matches (whether we find any is a different story)
348 #--------------------------------------------------------
350 """Return matches for aFragment at start of phrases."""
351 # self.__now = mxDT.now()
352 matches = gmDateTime.str2fuzzy_timestamp_matches(aFragment.strip())
353
354 if len(matches) == 0:
355 return (False, [])
356
357 items = []
358 for match in matches:
359 # if match['data'] is None:
360 # list_label = match['label']
361 # else:
362 # list_label = gmDateTime.pydt_strftime (
363 # match['data'].timestamp.format_accurately(),
364 # format = '%A, %d. %B %Y (%x)',
365 # accuracy = gmDateTime.acc_days
366 # )
367 items.append ({
368 'data': match['data'],
369 'field_label': match['label'],
370 'list_label': match['label']
371 })
372
373 return (True, items)
374 #--------------------------------------------------------
376 """Return matches for aFragment at start of words inside phrases."""
377 return self.getMatchesByPhrase(aFragment)
378 #--------------------------------------------------------
380 """Return matches for aFragment as a true substring."""
381 return self.getMatchesByPhrase(aFragment)
382 #--------------------------------------------------------
386 #==================================================
388
390
391 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
392
393 self.matcher = cMatchProvider_FuzzyTimestamp()
394 self.phrase_separators = None
395 self.selection_only = True
396 self.selection_only_error_msg = _('Cannot interpret input as timestamp.')
397 #--------------------------------------------------------
398 # internal helpers
399 #--------------------------------------------------------
401
402 if val is None:
403 val = self.GetValue().strip()
404
405 success, matches = self.matcher.getMatchesByPhrase(val)
406 if len(matches) == 1:
407 return matches[0]['data']
408
409 return None
410 #--------------------------------------------------------
411 # phrasewheel internal API
412 #--------------------------------------------------------
414 # are we valid ?
415 if self.data is None:
416 # no, so try
417 self.data = self.__text2timestamp()
418
419 # let the base class do its thing
420 gmPhraseWheel.cPhraseWheel._on_lose_focus(self, event)
421 #--------------------------------------------------------
423 data = item['data']
424 if data is not None:
425 return data.format_accurately()
426 return item['field_label']
427 #--------------------------------------------------------
428 # external API
429 #--------------------------------------------------------
431
432 if data is not None:
433 if isinstance(data, pyDT.datetime):
434 data = gmDateTime.cFuzzyTimestamp(timestamp=data)
435 if value.strip() == u'':
436 value = data.format_accurately()
437
438 gmPhraseWheel.cPhraseWheel.SetText(self, value = value, data = data, suppress_smarts = suppress_smarts)
439 #--------------------------------------------------------
441 if data is None:
442 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None)
443 else:
444 if isinstance(data, pyDT.datetime):
445 data = gmDateTime.cFuzzyTimestamp(timestamp=data)
446 gmPhraseWheel.cPhraseWheel.SetText(self, value = data.format_accurately(), data = data)
447 #--------------------------------------------------------
461 #==================================================
462 # main
463 #--------------------------------------------------
464 if __name__ == '__main__':
465
466 if len(sys.argv) < 2:
467 sys.exit()
468
469 if sys.argv[1] != 'test':
470 sys.exit()
471
472 gmI18N.activate_locale()
473 gmI18N.install_domain(domain='gnumed')
474 gmDateTime.init()
475
476 #----------------------------------------------------
478 mp = cMatchProvider_FuzzyTimestamp()
479 mp.word_separators = None
480 mp.setThresholds(aWord = 998, aSubstring = 999)
481 val = None
482 while val != 'exit':
483 print "************************************"
484 val = raw_input('Enter date fragment ("exit" to quit): ')
485 found, matches = mp.getMatches(aFragment=val)
486 for match in matches:
487 #print match
488 print match['label']
489 print match['data']
490 print "---------------"
491 #--------------------------------------------------------
493 app = wx.PyWidgetTester(size = (300, 40))
494 app.SetWidget(cFuzzyTimestampInput, id=-1, size=(180,20), pos=(10,20))
495 app.MainLoop()
496 #--------------------------------------------------------
498 app = wx.PyWidgetTester(size = (300, 40))
499 app.SetWidget(cDateInputPhraseWheel, id=-1, size=(180,20), pos=(10,20))
500 app.MainLoop()
501 #--------------------------------------------------------
502 #test_cli()
503 test_fuzzy_picker()
504 #test_picker()
505
506 #==================================================
507
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Jun 7 03:59:03 2011 | http://epydoc.sourceforge.net |