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 #============================================================75162 #============================================================77 78 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 79 self.phrase_separators = None 80 self.display_accuracy = None81 #-------------------------------------------------------- 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 if intv is None: 116 return u'' 117 return gmDateTime.format_interval ( 118 interval = intv, 119 accuracy_wanted = self.display_accuracy 120 )121 #-------------------------------------------------------- 122 # external API 123 #--------------------------------------------------------125 126 if isinstance(value, pyDT.timedelta): 127 self.SetText(data = value, suppress_smarts = True) 128 return 129 130 if value is None: 131 value = u'' 132 133 super(cIntervalPhraseWheel, self).SetValue(value)134 #--------------------------------------------------------136 137 if data is not None: 138 if value.strip() == u'': 139 value = gmDateTime.format_interval ( 140 interval = data, 141 accuracy_wanted = self.display_accuracy 142 ) 143 144 super(cIntervalPhraseWheel, self).SetText(value = value, data = data, suppress_smarts = suppress_smarts)145 #--------------------------------------------------------147 if data is None: 148 super(cIntervalPhraseWheel, self).SetText(u'', None) 149 return 150 151 value = gmDateTime.format_interval ( 152 interval = data, 153 accuracy_wanted = self.display_accuracy 154 ) 155 super(cIntervalPhraseWheel, self).SetText(value = value, data = data)156 #--------------------------------------------------------158 if len(self._data) == 0: 159 self._set_data_to_first_match() 160 161 return super(cIntervalPhraseWheel, self).GetData()164 """Shows a calendar control from which the user can pick a date."""210 211 #============================================================166 167 wx.Dialog.__init__(self, parent, title = _('Pick a date ...')) 168 panel = wx.Panel(self, -1) 169 170 sizer = wx.BoxSizer(wx.VERTICAL) 171 panel.SetSizer(sizer) 172 173 cal = wx.calendar.CalendarCtrl(panel) 174 175 if sys.platform != 'win32': 176 # gtk truncates the year - this fixes it 177 w, h = cal.Size 178 cal.Size = (w+25, h) 179 cal.MinSize = cal.Size 180 181 sizer.Add(cal, 0) 182 183 button_sizer = wx.BoxSizer(wx.HORIZONTAL) 184 button_sizer.Add((0, 0), 1) 185 btn_ok = wx.Button(panel, wx.ID_OK) 186 btn_ok.SetDefault() 187 button_sizer.Add(btn_ok, 0, wx.ALL, 2) 188 button_sizer.Add((0, 0), 1) 189 btn_can = wx.Button(panel, wx.ID_CANCEL) 190 button_sizer.Add(btn_can, 0, wx.ALL, 2) 191 button_sizer.Add((0, 0), 1) 192 sizer.Add(button_sizer, 1, wx.EXPAND | wx.ALL, 10) 193 sizer.Fit(panel) 194 self.ClientSize = panel.Size 195 196 cal.Bind(wx.EVT_KEY_DOWN, self.__on_key_down) 197 cal.SetFocus() 198 self.cal = cal199 #-----------------------------------------------------------213 """Turns strings into candidate dates. 214 215 Matching on "all" (*, '') will pop up a calendar :-) 216 """286 287 # # consider this: 288 # dlg = cCalendarDatePickerDlg(None) 289 # # FIXME: show below parent 290 # dlg.CentreOnScreen() 291 # 292 # if dlg.ShowModal() == wx.ID_OK: 293 # date = dlg.cal.Date 294 # if date is not None: 295 # if date.IsValid(): 296 # date = gmDateTime.wxDate2py_dt(wxDate = date).replace ( 297 # hour = 11, 298 # minute = 11, 299 # second = 11, 300 # microsecond = 111111 301 # ) 302 # lbl = gmDateTime.pydt_strftime(date, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days) 303 # matches = (True, [{'data': date, 'label': lbl}]) 304 # dlg.Destroy() 305 # 306 # return matches 307 #============================================================218 219 gmMatchProvider.cMatchProvider.__init__(self) 220 221 self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999) 222 self.word_separators = None223 # self.ignored_chars("""[?!."'\\(){}\[\]<>~#*$%^_]+""") 224 #-------------------------------------------------------- 225 # external API 226 #-------------------------------------------------------- 227 #-------------------------------------------------------- 228 # base class API 229 #-------------------------------------------------------- 230 # internal matching algorithms 231 # 232 # if we end up here: 233 # - aFragment will not be "None" 234 # - aFragment will be lower case 235 # - we _do_ deliver matches (whether we find any is a different story) 236 #--------------------------------------------------------238 """Return matches for aFragment at start of phrases.""" 239 matches = gmDateTime.str2pydt_matches(str2parse = aFragment.strip()) 240 241 if len(matches) == 0: 242 return (False, []) 243 244 items = [] 245 for match in matches: 246 if match['data'] is None: 247 items.append ({ 248 'data': None, 249 'field_label': match['label'], 250 'list_label': match['label'] 251 }) 252 continue 253 254 data = match['data'].replace ( 255 hour = 11, 256 minute = 11, 257 second = 11, 258 microsecond = 111111 259 ) 260 list_label = gmDateTime.pydt_strftime ( 261 data, 262 format = '%A, %d. %B %Y (%x)', 263 accuracy = gmDateTime.acc_days 264 ) 265 items.append ({ 266 'data': data, 267 'field_label': match['label'], 268 'list_label': list_label 269 }) 270 271 return (True, items)272 #--------------------------------------------------------274 """Return matches for aFragment at start of words inside phrases.""" 275 return self.getMatchesByPhrase(aFragment)276 #--------------------------------------------------------278 """Return matches for aFragment as a true substring.""" 279 return self.getMatchesByPhrase(aFragment)280 #--------------------------------------------------------309493 #============================================================311 312 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 313 314 self.matcher = cDateMatchProvider() 315 self.phrase_separators = None 316 317 self.static_tooltip_extra = _('<ALT-C/K>: pick from (c/k)alendar')318 #-------------------------------------------------------- 319 # internal helpers 320 #-------------------------------------------------------- 321 # def __text2timestamp(self): 322 # 323 # self._update_candidates_in_picklist(val = self.GetValue().strip()) 324 # 325 # if len(self._current_match_candidates) == 1: 326 # return self._current_match_candidates[0]['data'] 327 # 328 # return None 329 #--------------------------------------------------------331 dlg = cCalendarDatePickerDlg(self) 332 # FIXME: show below parent 333 dlg.CentreOnScreen() 334 decision = dlg.ShowModal() 335 date = dlg.cal.Date 336 dlg.Destroy() 337 338 if decision != wx.ID_OK: 339 return 340 341 if date is None: 342 return 343 344 if not date.IsValid(): 345 return 346 347 date = gmDateTime.wxDate2py_dt(wxDate = date).replace ( 348 hour = 11, 349 minute = 11, 350 second = 11, 351 microsecond = 111111 352 ) 353 val = gmDateTime.pydt_strftime(date, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days) 354 self.SetText(value = val, data = date, suppress_smarts = True)355 #-------------------------------------------------------- 356 # phrasewheel internal API 357 #--------------------------------------------------------359 # no valid date yet ? 360 if len(self._data) == 0: 361 self._set_data_to_first_match() 362 date = self.GetData() 363 if date is not None: 364 self.SetValue(gmDateTime.pydt_strftime(date, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)) 365 366 # let the base class do its thing 367 super(cDateInputPhraseWheel, self)._on_lose_focus(event)368 #--------------------------------------------------------370 data = item['data'] 371 if data is not None: 372 return gmDateTime.pydt_strftime(data, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days) 373 return item['field_label']374 #--------------------------------------------------------376 377 # <ALT-C> / <ALT-K> -> calendar 378 if event.AltDown() is True: 379 char = unichr(event.GetUnicodeKey()) 380 if char in u'ckCK': 381 self.__pick_from_calendar() 382 return 383 384 super(cDateInputPhraseWheel, self)._on_key_down(event)385 #--------------------------------------------------------387 if len(self._data) == 0: 388 return u'' 389 390 date = self.GetData() 391 # if match provider only provided completions 392 # but not a full date with it 393 if date is None: 394 return u'' 395 396 return gmDateTime.pydt_strftime ( 397 date, 398 format = '%A, %d. %B %Y (%x)', 399 accuracy = gmDateTime.acc_days 400 )401 #-------------------------------------------------------- 402 # external API 403 #--------------------------------------------------------405 406 if isinstance(value, pyDT.datetime): 407 date = value.replace ( 408 hour = 11, 409 minute = 11, 410 second = 11, 411 microsecond = 111111 412 ) 413 self.SetText(data = date, suppress_smarts = True) 414 return 415 416 if value is None: 417 value = u'' 418 419 super(self.__class__, self).SetValue(value)420 #--------------------------------------------------------422 423 if data is not None: 424 if isinstance(data, gmDateTime.cFuzzyTimestamp): 425 data = data.timestamp.replace ( 426 hour = 11, 427 minute = 11, 428 second = 11, 429 microsecond = 111111 430 ) 431 if value.strip() == u'': 432 value = gmDateTime.pydt_strftime(data, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days) 433 434 super(self.__class__, self).SetText(value = value, data = data, suppress_smarts = suppress_smarts)435 #--------------------------------------------------------437 if data is None: 438 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None) 439 return 440 self.SetText(data = data)441 #--------------------------------------------------------443 if len(self._data) == 0: 444 self._set_data_to_first_match() 445 446 return super(self.__class__, self).GetData()447 #--------------------------------------------------------449 if len(self._data) > 0: 450 self.display_as_valid(True) 451 return True 452 453 if self.GetValue().strip() == u'': 454 if allow_empty: 455 self.display_as_valid(True) 456 return True 457 else: 458 self.display_as_valid(False) 459 return False 460 461 # skip showing calendar on '*' from here 462 if self.GetValue().strip() == u'*': 463 self.display_as_valid(False) 464 return False 465 466 # try to auto-snap to first match 467 self._set_data_to_first_match() 468 if len(self._data) == 0: 469 self.display_as_valid(False) 470 return False 471 472 date = self.GetData() 473 self.SetValue(gmDateTime.pydt_strftime(date, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days)) 474 self.display_as_valid(True) 475 return True476 #-------------------------------------------------------- 477 # properties 478 #--------------------------------------------------------480 return self.GetData()481 484 # val = gmDateTime.pydt_strftime(date, format = '%Y-%m-%d', accuracy = gmDateTime.acc_days) 485 # self.data = date.replace ( 486 # hour = 11, 487 # minute = 11, 488 # second = 11, 489 # microsecond = 111111 490 # ) 491 492 date = property(_get_date, _set_date)545 #==================================================496 self.__allow_past = 1 497 self.__shifting_base = None 498 499 gmMatchProvider.cMatchProvider.__init__(self) 500 501 self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999) 502 self.word_separators = None503 # self.ignored_chars("""[?!."'\\(){}\[\]<>~#*$%^_]+""") 504 #-------------------------------------------------------- 505 # external API 506 #-------------------------------------------------------- 507 #-------------------------------------------------------- 508 # base class API 509 #-------------------------------------------------------- 510 # internal matching algorithms 511 # 512 # if we end up here: 513 # - aFragment will not be "None" 514 # - aFragment will be lower case 515 # - we _do_ deliver matches (whether we find any is a different story) 516 #--------------------------------------------------------518 """Return matches for aFragment at start of phrases.""" 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 items.append ({ 527 'data': match['data'], 528 'field_label': match['label'], 529 'list_label': match['label'] 530 }) 531 532 return (True, items)533 #--------------------------------------------------------535 """Return matches for aFragment at start of words inside phrases.""" 536 return self.getMatchesByPhrase(aFragment)537 #--------------------------------------------------------539 """Return matches for aFragment as a true substring.""" 540 return self.getMatchesByPhrase(aFragment)541 #--------------------------------------------------------547632 #================================================== 633 # main 634 #-------------------------------------------------- 635 if __name__ == '__main__': 636 637 if len(sys.argv) < 2: 638 sys.exit() 639 640 if sys.argv[1] != 'test': 641 sys.exit() 642 643 gmI18N.activate_locale() 644 gmI18N.install_domain(domain='gnumed') 645 gmDateTime.init() 646 647 #----------------------------------------------------549 550 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 551 552 self.matcher = cMatchProvider_FuzzyTimestamp() 553 self.phrase_separators = None 554 self.selection_only = True 555 self.selection_only_error_msg = _('Cannot interpret input as timestamp.') 556 self.display_accuracy = None557 #-------------------------------------------------------- 558 # internal helpers 559 #--------------------------------------------------------561 if val is None: 562 val = self.GetValue() 563 val = val.strip() 564 if val == u'': 565 return None 566 success, matches = self.matcher.getMatchesByPhrase(val) 567 if len(matches) == 1: 568 return matches[0]['data'] 569 return None570 #-------------------------------------------------------- 571 # phrasewheel internal API 572 #--------------------------------------------------------574 # are we valid ? 575 if self.data is None: 576 # no, so try 577 date = self.__text2timestamp() 578 if date is not None: 579 self.SetValue(value = date.format_accurately(accuracy = self.display_accuracy)) 580 self.data = date 581 582 # let the base class do its thing 583 gmPhraseWheel.cPhraseWheel._on_lose_focus(self, event)584 #--------------------------------------------------------586 data = item['data'] 587 if data is not None: 588 return data.format_accurately(accuracy = self.display_accuracy) 589 return item['field_label']590 #-------------------------------------------------------- 591 # external API 592 #--------------------------------------------------------594 595 if data is not None: 596 if isinstance(data, pyDT.datetime): 597 data = gmDateTime.cFuzzyTimestamp(timestamp=data) 598 if value.strip() == u'': 599 value = data.format_accurately(accuracy = self.display_accuracy) 600 601 gmPhraseWheel.cPhraseWheel.SetText(self, value = value, data = data, suppress_smarts = suppress_smarts)602 #--------------------------------------------------------604 if data is None: 605 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None) 606 else: 607 if isinstance(data, pyDT.datetime): 608 data = gmDateTime.cFuzzyTimestamp(timestamp=data) 609 gmPhraseWheel.cPhraseWheel.SetText(self, value = data.format_accurately(accuracy = self.display_accuracy), data = data)610 #--------------------------------------------------------612 if self.GetData() is not None: 613 return True 614 615 # skip empty value 616 if self.GetValue().strip() == u'': 617 if empty_is_valid: 618 return True 619 return False 620 621 date = self.__text2timestamp() 622 if date is None: 623 return False 624 625 self.SetText ( 626 value = date.format_accurately(accuracy = self.display_accuracy), 627 data = date, 628 suppress_smarts = True 629 ) 630 631 return True649 mp = cMatchProvider_FuzzyTimestamp() 650 mp.word_separators = None 651 mp.setThresholds(aWord = 998, aSubstring = 999) 652 val = None 653 while val != 'exit': 654 print "************************************" 655 val = raw_input('Enter date fragment ("exit" to quit): ') 656 found, matches = mp.getMatches(aFragment=val) 657 for match in matches: 658 #print match 659 print match['label'] 660 print match['data'] 661 print "---------------"662 #--------------------------------------------------------664 app = wx.PyWidgetTester(size = (300, 40)) 665 app.SetWidget(cFuzzyTimestampInput, id=-1, size=(180,20), pos=(10,20)) 666 app.MainLoop()667 #--------------------------------------------------------669 app = wx.PyWidgetTester(size = (300, 40)) 670 app.SetWidget(cDateInputPhraseWheel, id=-1, size=(180,20), pos=(10,20)) 671 app.MainLoop()672 #-------------------------------------------------------- 673 #test_cli() 674 #test_fuzzy_picker() 675 test_picker() 676 677 #================================================== 678
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Sat Aug 3 03:56:48 2013 | http://epydoc.sourceforge.net |