Package Gnumed :: Package wxpython :: Module gmDateTimeInput
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmDateTimeInput

  1  # -*- coding: iso-8859-1 -*- 
  2  """GNUmed date input widget 
  3   
  4  All GNUmed date input should happen via classes in 
  5  this module. Initially this is just a plain text box 
  6  but using this throughout GNUmed will allow us to 
  7  transparently add features. 
  8   
  9  @copyright: author(s) 
 10  """ 
 11  #============================================================================== 
 12  # $Source: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmDateTimeInput.py,v $ 
 13  # $Id: gmDateTimeInput.py,v 1.66 2009/12/21 15:04:57 ncq Exp $ 
 14  __version__ = "$Revision: 1.66 $" 
 15  __author__  = "K. Hilbert <Karsten.Hilbert@gmx.net>" 
 16  __licence__ = "GPL (details at http://www.gnu.org)" 
 17   
 18  import re, string, sys, time, datetime as pyDT, logging 
 19   
 20   
 21  import mx.DateTime as mxDT, wx 
 22   
 23   
 24  # GNUmed specific 
 25  if __name__ == '__main__': 
 26          sys.path.insert(0, '../../') 
 27  from Gnumed.pycommon import gmMatchProvider, gmDateTime 
 28  from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers 
 29   
 30  _log = logging.getLogger('gm.ui') 
 31   
 32  #============================================================ 
33 -class cMatchProvider_FuzzyTimestamp(gmMatchProvider.cMatchProvider):
34 - def __init__(self):
35 self.__allow_past = 1 36 self.__shifting_base = None 37 38 gmMatchProvider.cMatchProvider.__init__(self) 39 40 self.setThresholds(aPhrase = 1, aWord = 998, aSubstring = 999) 41 self.word_separators = None
42 # self.ignored_chars("""[?!."'\\(){}\[\]<>~#*$%^_]+""") 43 #-------------------------------------------------------- 44 # external API 45 #-------------------------------------------------------- 46 #-------------------------------------------------------- 47 # base class API 48 #-------------------------------------------------------- 49 # internal matching algorithms 50 # 51 # if we end up here: 52 # - aFragment will not be "None" 53 # - aFragment will be lower case 54 # - we _do_ deliver matches (whether we find any is a different story) 55 #--------------------------------------------------------
56 - def getMatchesByPhrase(self, aFragment):
57 """Return matches for aFragment at start of phrases.""" 58 self.__now = mxDT.now() 59 matches = gmDateTime.str2fuzzy_timestamp_matches(aFragment.strip()) 60 if len(matches) > 0: 61 return (True, matches) 62 else: 63 return (False, [])
64 #--------------------------------------------------------
65 - def getMatchesByWord(self, aFragment):
66 """Return matches for aFragment at start of words inside phrases.""" 67 return self.getMatchesByPhrase(aFragment)
68 #--------------------------------------------------------
69 - def getMatchesBySubstr(self, aFragment):
70 """Return matches for aFragment as a true substring.""" 71 return self.getMatchesByPhrase(aFragment)
72 #--------------------------------------------------------
73 - def getAllMatches(self):
74 """Return all items.""" 75 # FIXME: popup calendar to pick from 76 return (False, [])
77 #==================================================
78 -class cFuzzyTimestampInput(gmPhraseWheel.cPhraseWheel):
79
80 - def __init__(self, *args, **kwargs):
81 82 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 83 84 self.matcher = cMatchProvider_FuzzyTimestamp() 85 self.phrase_separators = None 86 self.selection_only = True 87 self.selection_only_error_msg = _('Cannot interpret input as timestamp.')
88 #-------------------------------------------------------- 89 # internal helpers 90 #--------------------------------------------------------
91 - def __text2timestamp(self, val=None):
92 93 if val is None: 94 val = self.GetValue().strip() 95 96 success, matches = self.matcher.getMatchesByPhrase(val) 97 #matches = gmDateTime.str2fuzzy_timestamp_matches(str2parse=val) 98 if len(matches) == 1: 99 return matches[0]['data'] 100 101 return None
102 #-------------------------------------------------------- 103 # phrasewheel internal API 104 #--------------------------------------------------------
105 - def _on_lose_focus(self, event):
106 # are we valid ? 107 if self.data is None: 108 # no, so try 109 self.data = self.__text2timestamp() 110 111 # let the base class do its thing 112 gmPhraseWheel.cPhraseWheel._on_lose_focus(self, event)
113 #--------------------------------------------------------
115 data = self._picklist.GetSelectedItemData() 116 if data is not None: 117 return data.format_accurately() 118 return self._picklist.get_selected_item_label()
119 #-------------------------------------------------------- 120 # external API 121 #--------------------------------------------------------
122 - def SetText(self, value=u'', data=None, suppress_smarts=False):
123 124 if data is not None: 125 if isinstance(data, pyDT.datetime): 126 data = gmDateTime.cFuzzyTimestamp(timestamp=data) 127 if value.strip() == u'': 128 value = data.format_accurately() 129 130 gmPhraseWheel.cPhraseWheel.SetText(self, value = value, data = data, suppress_smarts = suppress_smarts)
131 #--------------------------------------------------------
132 - def SetData(self, data=None):
133 if data is None: 134 gmPhraseWheel.cPhraseWheel.SetText(self, u'', None) 135 else: 136 if isinstance(data, pyDT.datetime): 137 data = gmDateTime.cFuzzyTimestamp(timestamp=data) 138 gmPhraseWheel.cPhraseWheel.SetText(self, value = data.format_accurately(), data = data)
139 #--------------------------------------------------------
140 - def is_valid_timestamp(self):
141 if self.data is not None: 142 return True 143 144 # skip empty value 145 if self.GetValue().strip() == u'': 146 return True 147 148 self.data = self.__text2timestamp() 149 if self.data is None: 150 return False 151 152 return True
153 #==================================================
154 -class cTimeInput(wx.TextCtrl):
155 - def __init__(self, parent, *args, **kwargs):
156 if len(args) < 2: 157 if not kwargs.has_key('value'): 158 kwargs['value'] = _('enter time here') 159 wx.TextCtrl.__init__( 160 self, 161 parent, 162 *args, 163 **kwargs 164 )
165 #==================================================
166 -class cDateInputCtrl(wx.DatePickerCtrl):
167
168 - def __init__(self, *args, **kwargs):
169 170 wx.DatePickerCtrl.__init__(self,*args,**kwargs)
171 #super(cDateInputCtrl, self).__init__(*args, **kwargs) 172 #self.Bind(wx.EVT_DATE_CHANGED, self.__on_date_changed, self) 173 #----------------------------------------------
174 - def SetValue(self, value):
175 """Set either datetime.datetime or wx.DateTime""" 176 177 if isinstance(value, (pyDT.date, pyDT.datetime)): 178 wxvalue = wx.DateTime() 179 wxvalue.Set(year = value.year, month = value.month-1, day = value.day) 180 value = wxvalue 181 182 elif value is None: 183 value = wx.DateTime() 184 185 wx.DatePickerCtrl.SetValue(self, value)
186 #----------------------------------------------
187 - def GetValue(self, as_pydt=False):
188 """Returns datetime.datetime values""" 189 190 value = wx.DatePickerCtrl.GetValue(self) 191 192 # manage null dates (useful when wx.DP_ALLOWNONE is set) 193 if not value.IsValid(): 194 return None 195 196 if not as_pydt: 197 return value 198 199 return pyDT.datetime(value.GetYear(), value.GetMonth() + 1, value.GetDay())
200 #---------------------------------------------- 201 # def convenience wrapper 202 #----------------------------------------------
203 - def is_valid_timestamp(self):
204 valid = self.GetValue().IsValid() 205 if valid: 206 self.SetBackgroundColour(gmPhraseWheel.color_prw_valid) 207 else: 208 self.SetBackgroundColour(gmPhraseWheel.color_prw_invalid) 209 self.Refresh() 210 return valid
211 #----------------------------------------------
212 - def get_pydt(self):
213 val = self.GetValue() 214 if val.IsValid(): 215 self.SetBackgroundColour(gmPhraseWheel.color_prw_valid) 216 val = gmDateTime.wxDate2py_dt(val) 217 self.Refresh() 218 else: 219 val = None 220 return val
221 #================================================== 222 # main 223 #-------------------------------------------------- 224 if __name__ == '__main__': 225 226 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'): 227 from Gnumed.pycommon import gmI18N 228 gmI18N.activate_locale() 229 gmI18N.install_domain(domain='gnumed') 230 gmDateTime.init() 231 232 #----------------------------------------------------
233 - def test_cli():
234 mp = cMatchProvider_FuzzyTimestamp() 235 mp.word_separators = None 236 mp.setThresholds(aWord = 998, aSubstring = 999) 237 val = None 238 while val != 'exit': 239 print "************************************" 240 val = raw_input('Enter date fragment: ') 241 found, matches = mp.getMatches(aFragment=val) 242 for match in matches: 243 print match['label'] 244 print match['data'] 245 print "---------------"
246 #--------------------------------------------------------
247 - def test_gui():
248 app = wx.PyWidgetTester(size = (200, 300)) 249 app.SetWidget(cFuzzyTimestampInput, id=-1, size=(180,20), pos=(10,20)) 250 app.MainLoop()
251 #--------------------------------------------------------
252 - def test_picker():
253 app = wx.PyWidgetTester(size = (200, 300)) 254 app.SetWidget(cDateInputCtrl, id=-1, size=(180,20), pos=(10,20)) 255 app.MainLoop()
256 #-------------------------------------------------------- 257 #test_cli() 258 #test_gui() 259 test_picker() 260 261 #================================================== 262 # - free text input: start string with " 263 #================================================== 264 # $Log: gmDateTimeInput.py,v $ 265 # Revision 1.66 2009/12/21 15:04:57 ncq 266 # - cDatePickerCtrl 267 # - SetValue now takes datetime, too 268 # - GetValue can return datetime 269 # 270 # Revision 1.65 2009/06/04 14:52:54 ncq 271 # - re-import lost cDatePickerCtrl and test 272 # 273 # Revision 1.65 2009/05/28 10:49:55 ncq 274 # - cDatePickerCtrl 275 # 276 # Revision 1.64 2009/02/05 14:28:46 ncq 277 # - cleanup 278 # 279 # Revision 1.63 2008/06/22 17:31:50 ncq 280 # - some cleanup 281 # 282 # Revision 1.62 2008/06/18 15:46:49 ncq 283 # - cleanup: offset/trigger chars are handled in the str 2 timestamp function directly 284 # 285 # Revision 1.61 2008/06/15 20:33:55 ncq 286 # - adjust to match provider properties 287 # 288 # Revision 1.60 2008/05/07 15:20:36 ncq 289 # - support suppress smarts in SetText 290 # 291 # Revision 1.59 2008/04/02 10:22:03 ncq 292 # - optionalize test suite running 293 # 294 # Revision 1.58 2008/03/05 22:30:13 ncq 295 # - new style logging 296 # 297 # Revision 1.57 2007/07/10 20:28:36 ncq 298 # - consolidate install_domain() args 299 # 300 # Revision 1.56 2007/06/15 10:24:53 ncq 301 # - adjust to change function signature 302 # 303 # Revision 1.55 2007/04/02 18:39:52 ncq 304 # - gmFuzzyTimestamp -> gmDateTime 305 # 306 # Revision 1.54 2007/03/18 14:01:52 ncq 307 # - re-add lost 1.54 308 # 309 # Revision 1.54 2007/03/12 12:26:15 ncq 310 # - allow SetData/SetText to take datetime.datetime instances 311 # 312 # Revision 1.53 2007/02/16 12:51:07 ncq 313 # - fix is_valid_timestamp() 314 # 315 # Revision 1.52 2007/02/16 10:23:44 ncq 316 # - make it selection_only and set error message 317 # - u''ify more 318 # - delegate more work to standard phrasewheel code 319 # - add SetText() 320 # - fix _picklist_selection2display_string() 321 # - need to activate locale in test suite 322 # 323 # Revision 1.51 2007/02/05 12:15:23 ncq 324 # - no more aMatchProvider/selection_only in cPhraseWheel.__init__() 325 # 326 # Revision 1.50 2007/02/04 15:51:00 ncq 327 # - no more snap_to_first_match 328 # - use SetText() 329 # - explicetly unset phrase separators 330 # 331 # Revision 1.49 2006/12/21 10:54:18 ncq 332 # - add SetData 333 # 334 # Revision 1.48 2006/11/27 23:14:33 ncq 335 # - remove prints 336 # 337 # Revision 1.47 2006/11/27 23:04:49 ncq 338 # - factor out UI-independant code 339 # 340 # Revision 1.46 2006/11/27 12:39:00 ncq 341 # - remove useless check 342 # 343 # Revision 1.45 2006/11/26 22:38:14 ncq 344 # - recognize 1953 as meaning that year :-) 345 # 346 # Revision 1.44 2006/11/24 14:19:43 ncq 347 # - variable name fix in __text2timestamp 348 # 349 # Revision 1.43 2006/11/24 10:01:31 ncq 350 # - gm_beep_statustext() -> gm_statustext() 351 # 352 # Revision 1.42 2006/11/19 11:11:57 ncq 353 # - fix wrong return value 354 # 355 # Revision 1.41 2006/07/19 20:29:50 ncq 356 # - import cleanup 357 # 358 # Revision 1.40 2006/07/01 13:12:32 ncq 359 # - improve test harness 360 # 361 # Revision 1.39 2006/06/15 15:35:30 ncq 362 # - better error handling 363 # 364 # Revision 1.38 2006/06/05 21:30:08 ncq 365 # - add single-dot expander so German 23. expands to 23rd this month this year 366 # - add is_valid_timestamp() to external API so patient wizard can use it 367 # 368 # Revision 1.37 2006/06/04 21:50:32 ncq 369 # - cleanup 370 # 371 # Revision 1.36 2006/06/02 13:17:50 ncq 372 # - add configurable offset chars for i18n 373 # - various cleanups and optimizations 374 # - fix __explicit_offset to use proper fuzzy timestamp 375 # - __validate() -> __text2timestamp() and smarten up 376 # 377 # Revision 1.35 2006/06/02 10:12:09 ncq 378 # - cleanup 379 # - add more fragment expanders 380 # 381 # Revision 1.34 2006/05/24 10:35:38 ncq 382 # - better named match provider 383 # 384 # Revision 1.33 2006/05/24 10:12:37 ncq 385 # - cleanup 386 # - timestamp match provider: 387 # - use fuzzy timestamp 388 # - i18n()ize single character triggers 389 # - improve phrasewheel strings 390 # - fuzzy timestamp phrasewheel 391 # - a lot of cleanup 392 # - proper test code 393 # 394 # Revision 1.32 2006/05/20 18:37:10 ncq 395 # - cleanup 396 # 397 # Revision 1.31 2006/05/12 12:08:51 ncq 398 # - comment out proposed fix for unicode problems 399 # 400 # Revision 1.30 2005/09/28 21:27:30 ncq 401 # - a lot of wx2.6-ification 402 # 403 # Revision 1.29 2005/09/28 15:57:47 ncq 404 # - a whole bunch of wx.Foo -> wx.Foo 405 # 406 # Revision 1.28 2005/09/26 18:01:50 ncq 407 # - use proper way to import wx26 vs wx2.4 408 # - note: THIS WILL BREAK RUNNING THE CLIENT IN SOME PLACES 409 # - time for fixup 410 # 411 # Revision 1.27 2005/09/25 01:13:06 ihaywood 412 # use a nicer way of discovering non-Unicode wxPython builds 413 # I resisted the temptation to use "if os.environ['USER'] == 'ncq':" 414 # 415 # Revision 1.26 2005/09/25 01:00:47 ihaywood 416 # bugfixes 417 # 418 # remember 2.6 uses "import wx" not "from wxPython import wx" 419 # removed not null constraint on clin_encounter.rfe as has no value on instantiation 420 # client doesn't try to set clin_encounter.description as it doesn't exist anymore 421 # 422 # Revision 1.25 2005/08/22 13:03:46 ncq 423 # - set bounds on "day of month" calculations 424 # 425 # Revision 1.24 2005/07/31 16:22:25 ncq 426 # - need to import "time" 427 # 428 # Revision 1.23 2005/07/31 16:04:19 ncq 429 # - on some platforms, notably MS Windows mx.DateTime does not support 430 # strptime(), hence use time.strptime() 431 # 432 # Revision 1.22 2005/07/31 15:32:50 ncq 433 # - cleanup 434 # 435 # Revision 1.21 2005/07/31 15:23:40 ncq 436 # - fixed long-standing validation logic bug 437 # - logging is best done using proper syntax, too 438 # 439 # Revision 1.20 2005/07/27 15:17:06 ncq 440 # - properly catch date input error such that we 441 # may find the bug on Windows 442 # 443 # Revision 1.19 2005/06/08 22:01:42 cfmoro 444 # Avoid validating when empty date 445 # 446 # Revision 1.18 2005/06/08 21:19:50 cfmoro 447 # Crash fix 448 # 449 # Revision 1.17 2005/06/04 09:55:32 ncq 450 # - also call parent class _on_lose_focus so we don't loose 451 # callbacks set by other people 452 # 453 # Revision 1.16 2005/06/03 00:54:33 cfmoro 454 # Validte date in SetValue 455 # 456 # Revision 1.15 2005/06/03 00:36:54 cfmoro 457 # Validate date on setValue 458 # 459 # Revision 1.14 2005/06/02 23:28:54 cfmoro 460 # Date validation 461 # 462 # Revision 1.13 2005/04/25 17:11:33 ncq 463 # - set encoding for file 464 # 465 # Revision 1.12 2005/04/24 15:05:22 ncq 466 # - use gmI18N properly 467 # 468 # Revision 1.11 2004/12/23 16:20:15 ncq 469 # - add licence 470 # 471 # Revision 1.10 2004/07/18 20:30:53 ncq 472 # - wxPython.true/false -> Python.True/False as Python tells us to do 473 # 474 # Revision 1.9 2004/03/05 11:22:35 ncq 475 # - import from Gnumed.<pkg> 476 # 477 # Revision 1.8 2004/02/25 09:46:21 ncq 478 # - import from pycommon now, not python-common 479 # 480 # Revision 1.7 2003/11/05 22:21:06 sjtan 481 # 482 # let's gmDateInput specify id_callback in constructor list. 483 # 484 # Revision 1.6 2003/11/04 10:35:23 ihaywood 485 # match providers in gmDemographicRecord 486 # 487 # Revision 1.5 2003/10/06 17:49:40 ncq 488 # - remove dependancy on gmI18N on standalone test run 489 # 490 # Revision 1.4 2003/10/02 20:51:12 ncq 491 # - add alt-XX shortcuts, move __* to _* 492 # 493 # Revision 1.3 2003/09/30 18:47:47 ncq 494 # - converted date time input field into phrase wheel descendant 495 # 496 # Revision 1.2 2003/08/10 00:57:15 ncq 497 # - add TODO item 498 # 499 # Revision 1.1 2003/05/23 14:05:01 ncq 500 # - first implementation 501 # 502