From 339983ff62d8d8e2cd4137dd61c46553d459cedc Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 22 Jul 2004 18:38:34 +0000 Subject: [PATCH] Patches from Will Sadkin: MaskedEditMixin: - fixed size calculations on changing fonts - fixed tabbing logic now that tab events are entered into the control by default (ie event.Skip()) if wx.TE_PROCESS_TAB is set - fixed code attempting to fix the selection after focus events generated on control destruction, to prevent tracebacks TextCtrl, ComboBox - Added support for XRC - Fixed sizing calculation code - Added SetFont() override method that will recalculate the size if this is called. - Added AppendItems() for ComboBox NumCtrl: - prevented ctrl from accepting same grouping and decimal character, - fixed issue preventing paste from working if decimal char was different than '.' TimeCtrl: - Fixed default value to use 24hour time (which will be converted appropriately if format supports it, and added code to check if local timezone uses "AM/PM" for this concept; if not, control now defaults to 24hour format, and disallows the am/pm form. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@28400 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/wx/lib/masked/__init__.py | 4 +- wxPython/wx/lib/masked/combobox.py | 98 +++++++++++++++++++++++++--- wxPython/wx/lib/masked/maskededit.py | 78 +++++++++++++++++----- wxPython/wx/lib/masked/numctrl.py | 43 +++++++----- wxPython/wx/lib/masked/textctrl.py | 47 ++++++++++++- wxPython/wx/lib/masked/timectrl.py | 42 +++++++++--- 6 files changed, 257 insertions(+), 55 deletions(-) diff --git a/wxPython/wx/lib/masked/__init__.py b/wxPython/wx/lib/masked/__init__.py index 1a5a59ea70..8e2c018745 100644 --- a/wxPython/wx/lib/masked/__init__.py +++ b/wxPython/wx/lib/masked/__init__.py @@ -12,8 +12,8 @@ # import relevant external symbols into package namespace: from maskededit import * -from textctrl import BaseMaskedTextCtrl, TextCtrl -from combobox import BaseMaskedComboBox, ComboBox, MaskedComboBoxSelectEvent +from textctrl import BaseMaskedTextCtrl, PreMaskedTextCtrl, TextCtrl +from combobox import BaseMaskedComboBox, PreMaskedComboBox, ComboBox, MaskedComboBoxSelectEvent from numctrl import NumCtrl, wxEVT_COMMAND_MASKED_NUMBER_UPDATED, EVT_NUM, NumberUpdatedEvent from timectrl import TimeCtrl, wxEVT_TIMEVAL_UPDATED, EVT_TIMEUPDATE, TimeUpdatedEvent from ipaddrctrl import IpAddrCtrl diff --git a/wxPython/wx/lib/masked/combobox.py b/wxPython/wx/lib/masked/combobox.py index c42db0c945..7e1ec0d0d5 100644 --- a/wxPython/wx/lib/masked/combobox.py +++ b/wxPython/wx/lib/masked/combobox.py @@ -54,11 +54,6 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ): **kwargs): - # This is necessary, because wxComboBox currently provides no - # method for determining later if this was specified in the - # constructor for the control... - self.__readonly = style & wx.CB_READONLY == wx.CB_READONLY - kwargs['choices'] = choices ## set up maskededit to work with choice list too ## Since combobox completion is case-insensitive, always validate same way @@ -80,15 +75,56 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ): choices=choices, style=style|wx.WANTS_CHARS, validator=validator, name=name) - self.controlInitialized = True + self._PostInit(style=style, setupEventHandling=setupEventHandling, + name=name, value=value, **kwargs) + + + def _PostInit(self, style=wx.CB_DROPDOWN, + setupEventHandling = True, ## setup event handling by default): + name = "maskedComboBox", value='', **kwargs): + + # This is necessary, because wxComboBox currently provides no + # method for determining later if this was specified in the + # constructor for the control... + self.__readonly = style & wx.CB_READONLY == wx.CB_READONLY + + if not hasattr(self, 'controlInitialized'): + + self.controlInitialized = True ## must have been called via XRC, therefore base class is constructed + if not kwargs.has_key('choices'): + choices=[] + kwargs['choices'] = choices ## set up maskededit to work with choice list too + self._choices = [] + + ## Since combobox completion is case-insensitive, always validate same way + if not kwargs.has_key('compareNoCase'): + kwargs['compareNoCase'] = True + + MaskedEditMixin.__init__( self, name, **kwargs ) + + self._choices = self._ctrl_constraints._choices +## dbg('self._choices:', self._choices) + + if self._ctrl_constraints._alignRight: + choices = [choice.rjust(self._masklength) for choice in choices] + else: + choices = [choice.ljust(self._masklength) for choice in choices] + wx.ComboBox.Clear(self) + wx.ComboBox.AppendItems(self, choices) + + # Set control font - fixed width by default self._setFont() if self._autofit: self.SetClientSize(self._CalcSize()) - self.SetSizeHints(self.GetSize()) + width = self.GetSize().width + height = self.GetBestSize().height + self.SetSize((width, height)) + self.SetSizeHints((width, height)) + if value: # ensure value is width of the mask of the control: @@ -134,6 +170,19 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ): return (size[0]+20, size[1]) + def SetFont(self, *args, **kwargs): + """ Set the font, then recalculate control size, if appropriate. """ + wx.ComboBox.SetFont(self, *args, **kwargs) + if self._autofit: + dbg('calculated size:', self._CalcSize()) + self.SetClientSize(self._CalcSize()) + width = self.GetSize().width + height = self.GetBestSize().height + dbg('setting client size to:', (width, height)) + self.SetSize((width, height)) + self.SetSizeHints((width, height)) + + def _GetSelection(self): """ Allow mixin to get the text selection of this control. @@ -306,7 +355,6 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ): else: wx.ComboBox.Undo() # else revert to base control behavior - def Append( self, choice, clientData=None ): """ This function override is necessary so we can keep track of any additions to the list @@ -352,6 +400,13 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ): wx.ComboBox.Append(self, choice, clientData) + def AppendItems( self, choices ): + """ + AppendItems() is handled in terms of Append, to avoid code replication. + """ + for choice in choices: + self.Append(choice) + def Clear( self ): """ @@ -544,3 +599,30 @@ class ComboBox( BaseMaskedComboBox, MaskedEditAccessorsMixin ): pass +class PreMaskedComboBox( BaseMaskedComboBox, MaskedEditAccessorsMixin ): + """ + This allows us to use XRC subclassing. + """ + # This should really be wx.EVT_WINDOW_CREATE but it is not + # currently delivered for native controls on all platforms, so + # we'll use EVT_SIZE instead. It should happen shortly after the + # control is created as the control is set to its "best" size. + _firstEventType = wx.EVT_SIZE + + def __init__(self): + pre = wx.PreComboBox() + self.PostCreate(pre) + self.Bind(self._firstEventType, self.OnCreate) + + + def OnCreate(self, evt): + self.Unbind(self._firstEventType) + self._PostInit() + +i=0 +## CHANGELOG: +## ==================== +## Version 1.1 +## 1. Added .SetFont() method that properly resizes control +## 2. Modified control to support construction via XRC mechanism. +## 3. Added AppendItems() to conform with latest combobox. diff --git a/wxPython/wx/lib/masked/maskededit.py b/wxPython/wx/lib/masked/maskededit.py index a271fc4a7b..4bafe63712 100644 --- a/wxPython/wx/lib/masked/maskededit.py +++ b/wxPython/wx/lib/masked/maskededit.py @@ -757,7 +757,7 @@ import wx from wx.tools.dbg import Logger dbg = Logger() -##dbg(enable=0) +##dbg(enable=1) ## ---------- ---------- ---------- ---------- ---------- ---------- ---------- @@ -1914,9 +1914,13 @@ class MaskedEditMixin: self._prevValue = newvalue # disallow undo of sign type if self._autofit: -## dbg('setting client size to:', self._CalcSize()) +## dbg('calculated size:', self._CalcSize()) self.SetClientSize(self._CalcSize()) - self.SetSizeHints(self.GetSize()) + width = self.GetSize().width + height = self.GetBestSize().height +## dbg('setting client size to:', (width, height)) + self.SetSize((width, height)) + self.SetSizeHints((width, height)) # Set value/type-specific formatting self._applyFormatting() @@ -1991,8 +1995,24 @@ class MaskedEditMixin: self._SetInitialValue() if self._autofit: + # this is tricky, because, as Robin explains: + # "Basically there are two sizes to deal with, that are potentially + # different. The client size is the inside size and may, depending + # on platform, exclude the borders and such. The normal size is + # the outside size that does include the borders. What you are + # calculating (in _CalcSize) is the client size, but the sizers + # deal with the full size and so that is the minimum size that + # we need to set with SetSizeHints. The root of the problem is + # that in _calcSize the current client size height is returned, + # instead of a height based on the current font. So I suggest using + # _calcSize to just get the width, and then use GetBestSize to + # get the height." self.SetClientSize(self._CalcSize()) - self.SetSizeHints(self.GetSize()) + width = self.GetSize().width + height = self.GetBestSize().height + self.SetSize((width, height)) + self.SetSizeHints((width, height)) + # Set value/type-specific formatting self._applyFormatting() @@ -2642,7 +2662,7 @@ class MaskedEditMixin: sizing_text += 'M' #### dbg('len(sizing_text):', len(sizing_text), 'sizing_text: "%s"' % sizing_text) w, h = self.GetTextExtent(sizing_text) - size = (w+4, self.GetClientSize().height) + size = (w+4, self.GetSize().height) #### dbg('size:', size, indent=0) return size @@ -2690,7 +2710,7 @@ class MaskedEditMixin: ## dbg('ignoring bogus text change event', indent=0) pass else: -## dbg('curvalue: "%s", newvalue: "%s"' % (self._curValue, newvalue)) +## dbg('curvalue: "%s", newvalue: "%s", len(newvalue): %d' % (self._curValue, newvalue, len(newvalue))) if self._Change(): if self._signOk and self._isNeg and newvalue.find('-') == -1 and newvalue.find('(') == -1: ## dbg('clearing self._isNeg') @@ -2864,6 +2884,8 @@ class MaskedEditMixin: if newfield != field and newfield._selectOnFieldEntry: ## dbg('queuing selection: (%d, %d)' % (newfield._extent[0], newfield._extent[1])) wx.CallAfter(self._SetSelection, newfield._extent[0], newfield._extent[1]) + else: + wx.CallAfter(self._SetSelection, newpos, new_select_to) keep_processing = False elif keep_processing: @@ -3400,12 +3422,12 @@ class MaskedEditMixin: def _OnReturn(self, event): """ - Changes the event to look like a tab event, so we can then call - event.Skip() on it, and have the parent form "do the right thing." + Swallows the return, issues a Navigate event instead, since + masked controls are "single line" by defn. """ ## dbg('MaskedEditMixin::OnReturn') - event.m_keyCode = wx.WXK_TAB - event.Skip() + self.Navigate(True) + return False def _OnHome(self,event): @@ -3486,9 +3508,6 @@ class MaskedEditMixin: """ Primarily handles TAB events, but can be used for any key that designer wants to change fields within a masked edit control. - NOTE: at the moment, although coded to handle shift-TAB and - control-shift-TAB, these events are not sent to the controls - by the framework. """ ## dbg('MaskedEditMixin::_OnChangeField', indent = 1) # determine end of current field: @@ -3500,7 +3519,10 @@ class MaskedEditMixin: self._AdjustField(pos) if event.GetKeyCode() == wx.WXK_TAB: ## dbg('tab to next ctrl') - event.Skip() + # As of 2.5.2, you don't call event.Skip() to do + # this, but instead force explicit navigation, if + # wx.TE_PROCESS_TAB is used (like in the masked edits) + self.Navigate(True) #else: do nothing ## dbg(indent=0) return False @@ -3534,7 +3556,10 @@ class MaskedEditMixin: self._AdjustField(pos) if event.GetKeyCode() == wx.WXK_TAB: ## dbg('tab to previous ctrl') - event.Skip() + # As of 2.5.2, you don't call event.Skip() to do + # this, but instead force explicit navigation, if + # wx.TE_PROCESS_TAB is used (like in the masked edits) + self.Navigate(False) else: ## dbg('position at beginning') wx.CallAfter(self._SetInsertionPoint, field_start) @@ -3580,7 +3605,10 @@ class MaskedEditMixin: self._AdjustField(pos) if event.GetKeyCode() == wx.WXK_TAB: ## dbg('tab to next ctrl') - event.Skip() + # As of 2.5.2, you don't call event.Skip() to do + # this, but instead force explicit navigation, if + # wx.TE_PROCESS_TAB is used (like in the masked edits) + self.Navigate(True) else: ## dbg('position at end') wx.CallAfter(self._SetInsertionPoint, field_end) @@ -3594,7 +3622,10 @@ class MaskedEditMixin: self._AdjustField(pos) if event.GetKeyCode() == wx.WXK_TAB: ## dbg('tab to next ctrl') - event.Skip() + # As of 2.5.2, you don't call event.Skip() to do + # this, but instead force explicit navigation, if + # wx.TE_PROCESS_TAB is used (like in the masked edits) + self.Navigate(True) #else: do nothing ## dbg(indent=0) return False @@ -3640,6 +3671,8 @@ class MaskedEditMixin: if fraction._selectOnFieldEntry: ## dbg('queuing selection after decimal point to:', (start, end)) wx.CallAfter(self._SetSelection, start, end) + else: + wx.CallAfter(self._SetSelection, start, start) keep_processing = False if self._isInt: ## handle integer value, truncate from current position @@ -3654,6 +3687,7 @@ class MaskedEditMixin: if newstr.find(')') != -1: newpos -= 1 # (don't move past right paren) wx.CallAfter(self._SetInsertionPoint, newpos) + wx.CallAfter(self._SetSelection, newpos, newpos) keep_processing = False ## dbg(indent=0) @@ -3949,6 +3983,7 @@ class MaskedEditMixin: pos = pos+2 if newvalue != value: +## dbg('old value: "%s"\nnew value: "%s"' % (value, newvalue)) self._SetValue(newvalue) self._SetInsertionPoint(pos) @@ -4042,6 +4077,8 @@ class MaskedEditMixin: self._SetInsertionPoint(pos) if pos < sel_to: # restore selection self._SetSelection(pos, sel_to) + else: + self._SetSelection(pos, pos) ## dbg('adjusted pos:', pos, indent=0) return pos @@ -5182,7 +5219,10 @@ class MaskedEditMixin: the control, and deselect. """ ## dbg('MaskedEditMixin::_fixSelection', indent=1) - if not self._mask or not self._IsEditable(): + # can get here if called with wx.CallAfter after underlying + # control has been destroyed on close, but after focus + # events + if not self or not self._mask or not self._IsEditable(): ## dbg(indent=0) return @@ -6396,6 +6436,8 @@ i=1 ## chars properly.) ## 4. Fixed autoselect behavior to work similarly to (2) above, so that combobox ## selection will only select the non-empty text, as per request. +## 5. Fixed tabbing to work with 2.5.2 semantics. +## 6. Fixed size calculation to handle changing fonts ## ## Version 1.6 ## 1. Reorganized masked controls into separate package, renamed things accordingly diff --git a/wxPython/wx/lib/masked/numctrl.py b/wxPython/wx/lib/masked/numctrl.py index f8f1934e1a..cfb64b22a2 100644 --- a/wxPython/wx/lib/masked/numctrl.py +++ b/wxPython/wx/lib/masked/numctrl.py @@ -386,7 +386,7 @@ MININT = -maxint-1 from wx.tools.dbg import Logger from wx.lib.masked import MaskedEditMixin, Field, BaseMaskedTextCtrl dbg = Logger() -##dbg(enable=0) +##dbg(enable=1) #---------------------------------------------------------------------------- @@ -654,14 +654,26 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin): maskededit_kwargs['mask'] = intmask+fracmask - if kwargs.has_key('groupChar'): + if kwargs.has_key('groupChar') or kwargs.has_key('decimalChar'): old_groupchar = self._groupChar # save so we can reformat properly -## dbg("old_groupchar: '%s'" % old_groupchar) - maskededit_kwargs['groupChar'] = kwargs['groupChar'] - if kwargs.has_key('decimalChar'): old_decimalchar = self._decimalChar +## dbg("old_groupchar: '%s'" % old_groupchar) ## dbg("old_decimalchar: '%s'" % old_decimalchar) - maskededit_kwargs['decimalChar'] = kwargs['decimalChar'] + groupchar = old_groupchar + decimalchar = old_decimalchar + + if kwargs.has_key('groupChar'): + maskededit_kwargs['groupChar'] = kwargs['groupChar'] + groupchar = kwargs['groupChar'] + if kwargs.has_key('decimalChar'): + maskededit_kwargs['decimalChar'] = kwargs['decimalChar'] + decimalchar = kwargs['decimalChar'] + + # Add sanity check to make sure these are distinct, and if not, + # raise attribute error + if groupchar == decimalchar: + raise AttributeError('groupChar and decimalChar must be distinct') + # for all other parameters, assign keyword args as appropriate: for key, param_value in kwargs.items(): @@ -1089,9 +1101,8 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin): and value[sel_start:sel_to] == self._groupChar ): self.SetInsertionPoint(sel_start) self.SetSelection(sel_start, sel_to+1) - - return BaseMaskedTextCtrl._OnErase(self, event, just_return_value) ## dbg(indent=0) + return BaseMaskedTextCtrl._OnErase(self, event, just_return_value) def OnTextChange( self, event ): @@ -1146,7 +1157,9 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin): A ValueError exception will be raised if an invalid value is specified. """ +## dbg('NumCtrl::SetValue(%s)' % value, indent=1) BaseMaskedTextCtrl.SetValue( self, self._toGUI(value) ) +## dbg(indent=0) def SetIntegerWidth(self, value): @@ -1519,7 +1532,7 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin): Preprocessor for base control paste; if value needs to be right-justified to fit in control, do so prior to paste: """ -## dbg('NumCtrl::_Paste (value = "%s")' % value) +## dbg('NumCtrl::_Paste (value = "%s")' % value, indent=1) if value is None: paste_text = self._getClipboardContents() else: @@ -1533,7 +1546,7 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin): # field = self._FindField(sel_start) edit_start, edit_end = field._extent - paste_text = paste_text.replace(self._groupChar, '').replace(self._decimalChar, '.').replace('(', '-').replace(')','') + paste_text = paste_text.replace(self._groupChar, '').replace('(', '-').replace(')','') if field._insertRight and self._groupDigits: # want to paste to the left; see if it will fit: left_text = old_value[edit_start:sel_start].lstrip() @@ -1547,8 +1560,8 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin): sel_start += sel_to - orig_sel_start # decrease by amount selected else: ## dbg("won't fit left;", 'paste text remains: "%s"' % paste_text) -## dbg('adjusted start before accounting for grouping:', sel_start) -## dbg('adjusted paste_text before accounting for grouping: "%s"' % paste_text) +## dbg('adjusted start before accounting for grouping:', sel_start) +## dbg('adjusted paste_text before accounting for grouping: "%s"' % paste_text) pass if self._groupDigits and sel_start != orig_sel_start: left_len = len(old_value[:sel_to].lstrip()) @@ -1564,12 +1577,6 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin): self.SetInsertionPoint(sel_to) self.SetSelection(sel_start, sel_to) -## # treat paste as "replace number", if appropriate: -## sel_start, sel_to = self._GetSelection() -## if sel_start == sel_to or self._selectOnEntry and (sel_start, sel_to) == self._fields[0]._extent: -## paste_text = self._toGUI(paste_text) -## self._SetSelection(0, len(self._mask)) - new_text, replace_to = MaskedEditMixin._Paste(self, paste_text, raise_on_invalid=raise_on_invalid, diff --git a/wxPython/wx/lib/masked/textctrl.py b/wxPython/wx/lib/masked/textctrl.py index 5f42128e81..f232e2266a 100644 --- a/wxPython/wx/lib/masked/textctrl.py +++ b/wxPython/wx/lib/masked/textctrl.py @@ -22,7 +22,7 @@ from wx.lib.masked import * # be a good place to implement the 2.3 logger class from wx.tools.dbg import Logger dbg = Logger() -##dbg(enable=0) +##dbg(enable=1) # ## TRICKY BIT: to avoid a ton of boiler-plate, and to # ## automate the getter/setter generation for each valid @@ -76,6 +76,13 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ): style=style, validator=validator, name=name) + self._PostInit(setupEventHandling = setupEventHandling, + name=name, value=value,**kwargs ) + + + def _PostInit(self,setupEventHandling=True, + name='maskedTextCtrl' , value='', **kwargs): + self.controlInitialized = True MaskedEditMixin.__init__( self, name, **kwargs ) @@ -219,6 +226,18 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ): wx.CallAfter(self._SetSelection, replace_to, replace_to) ## dbg(indent=0) + def SetFont(self, *args, **kwargs): + """ Set the font, then recalculate control size, if appropriate. """ + wx.TextCtrl.SetFont(self, *args, **kwargs) + if self._autofit: +## dbg('calculated size:', self._CalcSize()) + self.SetClientSize(self._CalcSize()) + width = self.GetSize().width + height = self.GetBestSize().height +## dbg('setting client size to:', (width, height)) + self.SetSize((width, height)) + self.SetSizeHints((width, height)) + def Clear(self): """ Blanks the current control value by replacing it with the default value.""" @@ -324,3 +343,29 @@ class TextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ): pass +class PreMaskedTextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ): + """ + This allows us to use XRC subclassing. + """ + # This should really be wx.EVT_WINDOW_CREATE but it is not + # currently delivered for native controls on all platforms, so + # we'll use EVT_SIZE instead. It should happen shortly after the + # control is created as the control is set to its "best" size. + _firstEventType = wx.EVT_SIZE + + def __init__(self): + pre = wx.PreTextCtrl() + self.PostCreate(pre) + self.Bind(self._firstEventType, self.OnCreate) + + + def OnCreate(self, evt): + self.Unbind(self._firstEventType) + self._PostInit() + +i=0 +## CHANGELOG: +## ==================== +## Version 1.1 +## 1. Added .SetFont() method that properly resizes control +## 2. Modified control to support construction via XRC mechanism. diff --git a/wxPython/wx/lib/masked/timectrl.py b/wxPython/wx/lib/masked/timectrl.py index 36fbbee97a..ababfc9185 100644 --- a/wxPython/wx/lib/masked/timectrl.py +++ b/wxPython/wx/lib/masked/timectrl.py @@ -59,7 +59,7 @@ Here's the API for TimeCtrl:
     TimeCtrl(
          parent, id = -1,
-         value = '12:00:00 AM',
+         value = '00:00:00',
          pos = wx.DefaultPosition,
          size = wx.DefaultSize,
          style = wxTE_PROCESS_TAB,
@@ -82,7 +82,10 @@ Here's the API for TimeCtrl:
     with SetValue() after instantiation of the control.)
     
size
The size of the control will be automatically adjusted for 12/24 hour format - if wx.DefaultSize is specified. + if wx.DefaultSize is specified. NOTE: due to a problem with wx.DateTime, if the + locale does not use 'AM/PM' for its values, the default format will automatically + change to 24 hour format, and an AttributeError will be thrown if a non-24 format + is specified.
style
By default, TimeCtrl will process TAB events, by allowing tab to the different cells within the control. @@ -95,7 +98,7 @@ Here's the API for TimeCtrl:
This parameter can be used instead of the fmt24hr and displaySeconds parameters, respectively; it provides a shorthand way to specify the time format you want. Accepted values are 'HHMMSS', 'HHMM', '24HHMMSS', and - '24HHMM'. If the format is specified, the other two arguments will be ignored. + '24HHMM'. If the format is specified, the other two arguments will be ignored.
fmt24hr
If True, control will display time in 24 hour time format; if False, it will @@ -337,7 +340,7 @@ class TimeCtrl(BaseMaskedTextCtrl): } def __init__ ( - self, parent, id=-1, value = '12:00:00 AM', + self, parent, id=-1, value = '00:00:00', pos = wx.DefaultPosition, size = wx.DefaultSize, fmt24hr=False, spinButton = None, @@ -348,6 +351,15 @@ class TimeCtrl(BaseMaskedTextCtrl): # set defaults for control: ## dbg('setting defaults:') + + self.__fmt24hr = False + wxdt = wx.DateTimeFromDMY(1, 0, 1970) + if wxdt.Format('%p') != 'AM': + TimeCtrl.valid_ctrl_params['format'] = '24HHMMSS' + self.__fmt24hr = True + fmt24hr = True # force/change default positional argument + # (will countermand explicit set to False too.) + for key, param_value in TimeCtrl.valid_ctrl_params.items(): # This is done this way to make setattr behave consistently with # "private attribute" name mangling @@ -367,7 +379,6 @@ class TimeCtrl(BaseMaskedTextCtrl): kwargs['displaySeconds'] = True # (handle positional arg (from original release) differently from rest of kwargs:) - self.__fmt24hr = False if not kwargs.has_key('format'): if fmt24hr: if kwargs.has_key('displaySeconds') and kwargs['displaySeconds']: @@ -449,7 +460,7 @@ class TimeCtrl(BaseMaskedTextCtrl): self.SetLimited(limited) self.SetValue(value) except: - self.SetValue('12:00:00 AM') + self.SetValue('00:00:00') if spinButton: self.BindSpinButton(spinButton) # bind spin button up/down events to this control @@ -472,6 +483,12 @@ class TimeCtrl(BaseMaskedTextCtrl): raise AttributeError('invalid keyword argument "%s"' % key) if key == 'format': + wxdt = wx.DateTimeFromDMY(1, 0, 1970) + if wxdt.Format('%p') != 'AM': + require24hr = True + else: + require24hr = False + # handle both local or generic 'maskededit' autoformat codes: if param_value == 'HHMMSS' or param_value == 'TIMEHHMMSS': self.__displaySeconds = True @@ -487,6 +504,10 @@ class TimeCtrl(BaseMaskedTextCtrl): self.__fmt24hr = True else: raise AttributeError('"%s" is not a valid format' % param_value) + + if require24hr and not self.__fmt24hr: + raise AttributeError('"%s" is an unsupported time format for the current locale' % param_value) + reset_format = True elif key in ("displaySeconds", "display_seconds") and not kwargs.has_key('format'): @@ -552,7 +573,7 @@ class TimeCtrl(BaseMaskedTextCtrl): self.SetLimited(limited) self.SetValue(value) except: - self.SetValue('12:00:00 AM') + self.SetValue('00:00:00') ## dbg(indent=0) return {} # no arguments to return else: @@ -663,8 +684,13 @@ class TimeCtrl(BaseMaskedTextCtrl): ## dbg('checkTime == len(value)?', valid) if not valid: + # deal with bug/deficiency in wx.DateTime: + if wxdt.Format('%p') not in ('AM', 'PM') and checkTime in (5,8): + # couldn't parse the AM/PM field + raise ValueError('cannot convert string "%s" to valid time for the current locale; please use 24hr time instead' % value) + else: ## dbg(indent=0, suspend=0) - raise ValueError('cannot convert string "%s" to valid time' % value) + raise ValueError('cannot convert string "%s" to valid time' % value) else: if isinstance(value, wx.DateTime):