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, gmGuiHelpers 30 31 _log = logging.getLogger('gm.ui') 32 33 #============================================================3581 82 #============================================================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 = cal70 #-----------------------------------------------------------139 #============================================================85 86 gmMatchProvider.cMatchProvider.__init__(self) 87 88 self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999) 89 self.word_separators = None90 # 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 matches141269 270 #============================================================143 144 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 145 146 self.matcher = cDateMatchProvider() 147 self.phrase_separators = None148 # 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 None164 #--------------------------------------------------------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 # <ALT-F4> -> calendar 205 if event.AltDown() is False: 206 keycode = event.GetKeyCode() 207 if keycode == wx.WXK_F4: 208 self.__pick_from_calendar() 209 return 210 211 super(self.__class__, self)._on_key_down(event)212 #--------------------------------------------------------214 if self.data is None: 215 return u'' 216 217 return self.data.strftime('%A, %d. %B %Y (%x)').decode(gmI18N.get_encoding())218 #-------------------------------------------------------- 219 # external API 220 #--------------------------------------------------------222 223 if isinstance(value, pyDT.datetime): 224 self.SetText(data = value, suppress_smarts = True) 225 return 226 227 if value is None: 228 value = u'' 229 230 super(self.__class__, self).SetValue(value)231 #--------------------------------------------------------233 234 if data is not None: 235 if isinstance(data, gmDateTime.cFuzzyTimestamp): 236 data = data.timestamp 237 if value.strip() == u'': 238 value = data.strftime('%Y-%m-%d') 239 240 super(self.__class__, self).SetText(value = value, data = data, suppress_smarts = suppress_smarts)241 #--------------------------------------------------------243 if data is None: 244 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None) 245 else: 246 if isinstance(data, gmDateTime.cFuzzyTimestamp): 247 data = data.timestamp 248 super(self.__class__, self).SetText(value = data.strftime('%Y-%m-%d'), data = data)249 #--------------------------------------------------------251 if self.data is None: 252 self.data = self.__text2timestamp() 253 254 return super(self.__class__, self).GetData()255 #--------------------------------------------------------315 #==================================================273 self.__allow_past = 1 274 self.__shifting_base = None 275 276 gmMatchProvider.cMatchProvider.__init__(self) 277 278 self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999) 279 self.word_separators = None280 # self.ignored_chars("""[?!."'\\(){}\[\]<>~#*$%^_]+""") 281 #-------------------------------------------------------- 282 # external API 283 #-------------------------------------------------------- 284 #-------------------------------------------------------- 285 # base class API 286 #-------------------------------------------------------- 287 # internal matching algorithms 288 # 289 # if we end up here: 290 # - aFragment will not be "None" 291 # - aFragment will be lower case 292 # - we _do_ deliver matches (whether we find any is a different story) 293 #--------------------------------------------------------295 """Return matches for aFragment at start of phrases.""" 296 self.__now = mxDT.now() 297 matches = gmDateTime.str2fuzzy_timestamp_matches(aFragment.strip()) 298 if len(matches) > 0: 299 return (True, matches) 300 else: 301 return (False, [])302 #--------------------------------------------------------304 """Return matches for aFragment at start of words inside phrases.""" 305 return self.getMatchesByPhrase(aFragment)306 #--------------------------------------------------------308 """Return matches for aFragment as a true substring.""" 309 return self.getMatchesByPhrase(aFragment)310 #--------------------------------------------------------317390 #================================================== 402 #==================================================319 320 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 321 322 self.matcher = cMatchProvider_FuzzyTimestamp() 323 self.phrase_separators = None 324 self.selection_only = True 325 self.selection_only_error_msg = _('Cannot interpret input as timestamp.')326 #-------------------------------------------------------- 327 # internal helpers 328 #--------------------------------------------------------330 331 if val is None: 332 val = self.GetValue().strip() 333 334 success, matches = self.matcher.getMatchesByPhrase(val) 335 if len(matches) == 1: 336 return matches[0]['data'] 337 338 return None339 #-------------------------------------------------------- 340 # phrasewheel internal API 341 #--------------------------------------------------------343 # are we valid ? 344 if self.data is None: 345 # no, so try 346 self.data = self.__text2timestamp() 347 348 # let the base class do its thing 349 gmPhraseWheel.cPhraseWheel._on_lose_focus(self, event)350 #--------------------------------------------------------352 data = self._picklist.GetSelectedItemData() 353 if data is not None: 354 return data.format_accurately() 355 return self._picklist.get_selected_item_label()356 #-------------------------------------------------------- 357 # external API 358 #--------------------------------------------------------360 361 if data is not None: 362 if isinstance(data, pyDT.datetime): 363 data = gmDateTime.cFuzzyTimestamp(timestamp=data) 364 if value.strip() == u'': 365 value = data.format_accurately() 366 367 gmPhraseWheel.cPhraseWheel.SetText(self, value = value, data = data, suppress_smarts = suppress_smarts)368 #--------------------------------------------------------370 if data is None: 371 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None) 372 else: 373 if isinstance(data, pyDT.datetime): 374 data = gmDateTime.cFuzzyTimestamp(timestamp=data) 375 gmPhraseWheel.cPhraseWheel.SetText(self, value = data.format_accurately(), data = data)376 #--------------------------------------------------------404 405 #----------------------------------------------476 #================================================== 477 # main 478 #-------------------------------------------------- 479 if __name__ == '__main__': 480 481 if len(sys.argv) < 2: 482 sys.exit() 483 484 if sys.argv[2] != 'test': 485 sys.exit() 486 487 gmI18N.activate_locale() 488 gmI18N.install_domain(domain='gnumed') 489 gmDateTime.init() 490 491 #----------------------------------------------------407 """Set either datetime.datetime or wx.DateTime""" 408 409 if isinstance(value, (pyDT.date, pyDT.datetime)): 410 value = gmDateTime.py_dt2wxDate(py_dt = value, wx = wx) 411 412 elif value is None: 413 value = wx.DefaultDateTime 414 415 wx.DatePickerCtrl.SetValue(self, value)416 #----------------------------------------------418 """Returns datetime.datetime values""" 419 420 # datepicker can fail to pick up user changes by keyboard until 421 # it has lost focus, so do that but also set the focus back to us, 422 # now, this is a side-effect (after .GetValue focus will be 423 # here) but at least it is predictable ... 424 self.Navigate() 425 self.SetFocus() 426 value = wx.DatePickerCtrl.GetValue(self) 427 428 if value is None: 429 return None 430 431 # manage null dates (useful when wx.DP_ALLOWNONE is set) 432 if not value.IsValid(): 433 if invalid_as_none: 434 return None 435 else: 436 return value 437 438 self.SetBackgroundColour(gmPhraseWheel.color_prw_valid) 439 self.Refresh() 440 441 if not as_pydt: 442 return value 443 444 return gmDateTime.wxDate2py_dt(value)445 #---------------------------------------------- 446 # def convenience wrapper 447 #----------------------------------------------449 val = self.GetValue(as_pydt = False, invalid_as_none = invalid_as_none) 450 451 if val is None: 452 if allow_none: 453 valid = True 454 else: 455 valid = False 456 else: 457 valid = val.IsValid() 458 459 if valid: 460 self.SetBackgroundColour(gmPhraseWheel.color_prw_valid) 461 else: 462 self.SetBackgroundColour(gmPhraseWheel.color_prw_invalid) 463 464 self.Refresh() 465 return valid466 #----------------------------------------------468 return self.GetValue(as_pydt = True)469 #----------------------------------------------471 if valid is True: 472 self.SetBackgroundColour(gmPhraseWheel.color_prw_valid) 473 else: 474 self.SetBackgroundColour(gmPhraseWheel.color_prw_invalid) 475 self.Refresh()493 mp = cMatchProvider_FuzzyTimestamp() 494 mp.word_separators = None 495 mp.setThresholds(aWord = 998, aSubstring = 999) 496 val = None 497 while val != 'exit': 498 print "************************************" 499 val = raw_input('Enter date fragment: ') 500 found, matches = mp.getMatches(aFragment=val) 501 for match in matches: 502 print match['label'] 503 print match['data'] 504 print "---------------"505 #--------------------------------------------------------507 app = wx.PyWidgetTester(size = (200, 300)) 508 app.SetWidget(cFuzzyTimestampInput, id=-1, size=(180,20), pos=(10,20)) 509 app.MainLoop()510 #--------------------------------------------------------512 app = wx.PyWidgetTester(size = (200, 300)) 513 app.SetWidget(cDateInputCtrl, id=-1, size=(180,20), pos=(10,20)) 514 app.MainLoop()515 #-------------------------------------------------------- 516 #test_cli() 517 #test_gui() 518 test_picker() 519 520 #================================================== 521
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Thu Mar 17 03:57:12 2011 | http://epydoc.sourceforge.net |