" % self.GetValue()
def _GetSelection(self):
@@ -5750,14 +5879,14 @@ class wxMaskedTextCtrl( wxTextCtrl, wxMaskedEditMixin ):
Allow mixin to set the text selection of this control.
REQUIRED by any class derived from wxMaskedEditMixin.
"""
-## dbg("wxMaskedTextCtrl::_SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
+#### dbg("wxMaskedTextCtrl::_SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
return self.SetSelection( sel_start, sel_to )
def SetSelection(self, sel_start, sel_to):
"""
This is just for debugging...
"""
- dbg("wxMaskedTextCtrl::SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
+## dbg("wxMaskedTextCtrl::SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
wxTextCtrl.SetSelection(self, sel_start, sel_to)
@@ -5765,14 +5894,14 @@ class wxMaskedTextCtrl( wxTextCtrl, wxMaskedEditMixin ):
return self.GetInsertionPoint()
def _SetInsertionPoint(self, pos):
-## dbg("wxMaskedTextCtrl::_SetInsertionPoint(%(pos)d)" % locals())
+#### dbg("wxMaskedTextCtrl::_SetInsertionPoint(%(pos)d)" % locals())
self.SetInsertionPoint(pos)
def SetInsertionPoint(self, pos):
"""
This is just for debugging...
"""
- dbg("wxMaskedTextCtrl::SetInsertionPoint(%(pos)d)" % locals())
+## dbg("wxMaskedTextCtrl::SetInsertionPoint(%(pos)d)" % locals())
wxTextCtrl.SetInsertionPoint(self, pos)
@@ -5788,12 +5917,12 @@ class wxMaskedTextCtrl( wxTextCtrl, wxMaskedEditMixin ):
Allow mixin to set the raw value of the control with this function.
REQUIRED by any class derived from wxMaskedEditMixin.
"""
- dbg('wxMaskedTextCtrl::_SetValue("%(value)s")' % locals(), indent=1)
+## dbg('wxMaskedTextCtrl::_SetValue("%(value)s")' % locals(), indent=1)
# Record current selection and insertion point, for undo
self._prevSelection = self._GetSelection()
self._prevInsertionPoint = self._GetInsertionPoint()
wxTextCtrl.SetValue(self, value)
- dbg(indent=0)
+## dbg(indent=0)
def SetValue(self, value):
"""
@@ -5802,7 +5931,7 @@ class wxMaskedTextCtrl( wxTextCtrl, wxMaskedEditMixin ):
masked control. NOTE: this must be done in the class derived
from the base wx control.
"""
- dbg('wxMaskedTextCtrl::SetValue = "%s"' % value, indent=1)
+## dbg('wxMaskedTextCtrl::SetValue = "%s"' % value, indent=1)
if not self._mask:
wxTextCtrl.SetValue(self, value) # revert to base control behavior
@@ -5822,12 +5951,12 @@ class wxMaskedTextCtrl( wxTextCtrl, wxMaskedEditMixin ):
and (self._isFloat or self._isInt) # and it's a numeric control
and self._ctrl_constraints._alignRight ): # and it's a right-aligned control
- dbg('len(value)', len(value), ' < self._masklength', self._masklength)
+## dbg('len(value)', len(value), ' < self._masklength', self._masklength)
# try to intelligently "pad out" the value to the right size:
value = self._template[0:self._masklength - len(value)] + value
if self._isFloat and value.find('.') == -1:
value = value[1:]
- dbg('padded value = "%s"' % value)
+## dbg('padded value = "%s"' % value)
# make SetValue behave the same as if you had typed the value in:
try:
@@ -5846,22 +5975,23 @@ class wxMaskedTextCtrl( wxTextCtrl, wxMaskedEditMixin ):
dateparts = value.split(' ')
dateparts[0] = self._adjustDate(dateparts[0], fixcentury=true)
value = string.join(dateparts, ' ')
- dbg('adjusted value: "%s"' % value)
+## dbg('adjusted value: "%s"' % value)
value = self._Paste(value, raise_on_invalid=True, just_return_value=True)
else:
- dbg('exception thrown', indent=0)
+## dbg('exception thrown', indent=0)
raise
- self._SetValue(value)
-## dbg('queuing insertion after .SetValue', self._masklength)
+ self._SetValue(value) # note: to preserve similar capability, .SetValue()
+ # does not change IsModified()
+#### dbg('queuing insertion after .SetValue', self._masklength)
wxCallAfter(self._SetInsertionPoint, self._masklength)
wxCallAfter(self._SetSelection, self._masklength, self._masklength)
- dbg(indent=0)
+## dbg(indent=0)
def Clear(self):
""" Blanks the current control value by replacing it with the default value."""
- dbg("wxMaskedTextCtrl::Clear - value reset to default value (template)")
+## dbg("wxMaskedTextCtrl::Clear - value reset to default value (template)")
if self._mask:
self.ClearValue()
else:
@@ -5873,9 +6003,9 @@ class wxMaskedTextCtrl( wxTextCtrl, wxMaskedEditMixin ):
Allow mixin to refresh the base control with this function.
REQUIRED by any class derived from wxMaskedEditMixin.
"""
- dbg('wxMaskedTextCtrl::_Refresh', indent=1)
+## dbg('wxMaskedTextCtrl::_Refresh', indent=1)
wxTextCtrl.Refresh(self)
- dbg(indent=0)
+## dbg(indent=0)
def Refresh(self):
@@ -5884,10 +6014,10 @@ class wxMaskedTextCtrl( wxTextCtrl, wxMaskedEditMixin ):
validate the contents of the masked control as it refreshes.
NOTE: this must be done in the class derived from the base wx control.
"""
- dbg('wxMaskedTextCtrl::Refresh', indent=1)
+## dbg('wxMaskedTextCtrl::Refresh', indent=1)
self._CheckValid()
self._Refresh()
- dbg(indent=0)
+## dbg(indent=0)
def _IsEditable(self):
@@ -5953,6 +6083,16 @@ class wxMaskedTextCtrl( wxTextCtrl, wxMaskedEditMixin ):
return self._calcSize(size)
+class wxMaskedTextCtrl( wxBaseMaskedTextCtrl, wxMaskedEditAccessorsMixin ):
+ """
+ This extra level of inheritance allows us to add the generic set of
+ masked edit parameters only to this class while allowing other
+ classes to derive from the "base" masked text control, and provide
+ a smaller set of valid accessor functions.
+ """
+ pass
+
+
## ---------- ---------- ---------- ---------- ---------- ---------- ----------
## Because calling SetSelection programmatically does not fire EVT_COMBOBOX
## events, we have to do it ourselves when we auto-complete.
@@ -5969,7 +6109,7 @@ class wxMaskedComboBoxSelectEvent(wxPyCommandEvent):
return self.__selection
-class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
+class wxBaseMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
"""
This masked edit control adds the ability to use a masked input
on a combobox, and do auto-complete of such values.
@@ -5997,8 +6137,9 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
kwargs['compareNoCase'] = True
wxMaskedEditMixin.__init__( self, name, **kwargs )
+
self._choices = self._ctrl_constraints._choices
- dbg('self._choices:', self._choices)
+## dbg('self._choices:', self._choices)
if self._ctrl_constraints._alignRight:
choices = [choice.rjust(self._masklength) for choice in choices]
@@ -6134,7 +6275,7 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
and self._ctrl_constraints._alignRight ): # and it's a right-aligned control
# try to intelligently "pad out" the value to the right size:
value = self._template[0:self._masklength - len(value)] + value
- dbg('padded value = "%s"' % value)
+## dbg('padded value = "%s"' % value)
# For wxComboBox, ensure that values are properly padded so that
# if varying length choices are supplied, they always show up
@@ -6163,13 +6304,13 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
dateparts = value.split(' ')
dateparts[0] = self._adjustDate(dateparts[0], fixcentury=true)
value = string.join(dateparts, ' ')
- dbg('adjusted value: "%s"' % value)
+## dbg('adjusted value: "%s"' % value)
value = self._Paste(value, raise_on_invalid=True, just_return_value=True)
else:
raise
self._SetValue(value)
-## dbg('queuing insertion after .SetValue', self._masklength)
+#### dbg('queuing insertion after .SetValue', self._masklength)
wxCallAfter(self._SetInsertionPoint, self._masklength)
wxCallAfter(self._SetSelection, self._masklength, self._masklength)
@@ -6266,7 +6407,7 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
choice = choice.ljust(self._masklength)
if self._ctrl_constraints._fillChar != ' ':
choice = choice.replace(' ', self._fillChar)
- dbg('updated choice:', choice)
+## dbg('updated choice:', choice)
self._ctrl_constraints._compareChoices.append(compareChoice)
@@ -6295,14 +6436,12 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
wxComboBox.Clear(self)
- def SetCtrlParameters( self, **kwargs ):
+ def _OnCtrlParametersChanged(self):
"""
- Override mixin's default SetCtrlParameters to detect changes in choice list, so
+ Override mixin's default OnCtrlParametersChanged to detect changes in choice list, so
we can update the base control:
"""
- wxMaskedEditMixin.SetCtrlParameters(self, **kwargs )
- if( self.controlInitialized
- and (kwargs.has_key('choices') or self._choices != self._ctrl_constraints._choices) ):
+ if self.controlInitialized and self._choices != self._ctrl_constraints._choices:
wxComboBox.Clear(self)
self._choices = self._ctrl_constraints._choices
for choice in self._choices:
@@ -6316,25 +6455,25 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
works, but has the nasty side effect of generating lots of intermediate
events.
"""
- dbg(suspend=1) # turn off debugging around this function
- dbg('wxMaskedComboBox::GetMark', indent=1)
+## dbg(suspend=1) # turn off debugging around this function
+## dbg('wxMaskedComboBox::GetMark', indent=1)
if self.__readonly:
- dbg(indent=0)
+## dbg(indent=0)
return 0, 0 # no selection possible for editing
## sel_start, sel_to = wxComboBox.GetMark(self) # what I'd *like* to have!
sel_start = sel_to = self.GetInsertionPoint()
- dbg("current sel_start:", sel_start)
+## dbg("current sel_start:", sel_start)
value = self.GetValue()
- dbg('value: "%s"' % value)
+## dbg('value: "%s"' % value)
self._ignoreChange = True # tell _OnTextChange() to ignore next event (if any)
wxComboBox.Cut(self)
newvalue = self.GetValue()
- dbg("value after Cut operation:", newvalue)
+## dbg("value after Cut operation:", newvalue)
if newvalue != value: # something was selected; calculate extent
- dbg("something selected")
+## dbg("something selected")
sel_to = sel_start + len(value) - len(newvalue)
wxComboBox.SetValue(self, value) # restore original value and selection (still ignoring change)
wxComboBox.SetInsertionPoint(self, sel_start)
@@ -6342,7 +6481,7 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
self._ignoreChange = False # tell _OnTextChange() to pay attn again
- dbg('computed selection:', sel_start, sel_to, indent=0, suspend=0)
+## dbg('computed selection:', sel_start, sel_to, indent=0, suspend=0)
return sel_start, sel_to
@@ -6351,7 +6490,7 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
Necessary for bookkeeping on choice selection, to keep current value
current.
"""
- dbg('wxMaskedComboBox::SetSelection(%d)' % index)
+## dbg('wxMaskedComboBox::SetSelection(%d)' % index)
if self._mask:
self._prevValue = self._curValue
self._curValue = self._choices[index]
@@ -6379,7 +6518,7 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
on the text of the control somehow interferes with the combobox's
selection mechanism for the arrow keys.
"""
- dbg('wxMaskedComboBox::OnSelectChoice', indent=1)
+## dbg('wxMaskedComboBox::OnSelectChoice', indent=1)
if not self._mask:
event.Skip()
@@ -6401,7 +6540,7 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
self._ctrl_constraints._compareNoCase,
current_index = self._ctrl_constraints._autoCompleteIndex)
if match_index is not None:
- dbg('setting selection to', match_index)
+## dbg('setting selection to', match_index)
# issue appropriate event to outside:
self._OnAutoSelect(self._ctrl_constraints, match_index=match_index)
self._CheckValid()
@@ -6410,15 +6549,15 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
field = self._FindField(pos)
if self.IsEmpty() or not field._hasList:
- dbg('selecting 1st value in list')
+## dbg('selecting 1st value in list')
self._OnAutoSelect(self._ctrl_constraints, match_index=0)
self._CheckValid()
keep_processing = False
else:
# attempt field-level auto-complete
- dbg(indent=0)
+## dbg(indent=0)
keep_processing = self._OnAutoCompleteField(event)
- dbg('keep processing?', keep_processing, indent=0)
+## dbg('keep processing?', keep_processing, indent=0)
return keep_processing
@@ -6427,17 +6566,17 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
Override mixin (empty) autocomplete handler, so that autocompletion causes
combobox to update appropriately.
"""
- dbg('wxMaskedComboBox::OnAutoSelect', field._index, indent=1)
+## dbg('wxMaskedComboBox::OnAutoSelect', field._index, indent=1)
## field._autoCompleteIndex = match_index
if field == self._ctrl_constraints:
self.SetSelection(match_index)
- dbg('issuing combo selection event')
+## dbg('issuing combo selection event')
self.GetEventHandler().ProcessEvent(
wxMaskedComboBoxSelectEvent( self.GetId(), match_index, self ) )
self._CheckValid()
- dbg('field._autoCompleteIndex:', match_index)
- dbg('self.GetSelection():', self.GetSelection())
- dbg(indent=0)
+## dbg('field._autoCompleteIndex:', match_index)
+## dbg('self.GetSelection():', self.GetSelection())
+## dbg(indent=0)
def _OnReturn(self, event):
@@ -6450,25 +6589,76 @@ class wxMaskedComboBox( wxComboBox, wxMaskedEditMixin ):
programmatic wxComboBox.SetSelection() call to pick the appropriate
item in the list. (and then do the usual OnReturn bit.)
"""
- dbg('wxMaskedComboBox::OnReturn', indent=1)
- dbg('current value: "%s"' % self.GetValue(), 'current index:', self.GetSelection())
+## dbg('wxMaskedComboBox::OnReturn', indent=1)
+## dbg('current value: "%s"' % self.GetValue(), 'current index:', self.GetSelection())
if self.GetSelection() == -1 and self.GetValue().lower().strip() in self._ctrl_constraints._compareChoices:
wxCallAfter(self.SetSelection, self._ctrl_constraints._autoCompleteIndex)
event.m_keyCode = WXK_TAB
event.Skip()
- dbg(indent=0)
+## dbg(indent=0)
+
+
+class wxMaskedComboBox( wxBaseMaskedComboBox, wxMaskedEditAccessorsMixin ):
+ """
+ This extra level of inheritance allows us to add the generic set of
+ masked edit parameters only to this class while allowing other
+ classes to derive from the "base" masked combobox control, and provide
+ a smaller set of valid accessor functions.
+ """
+ pass
## ---------- ---------- ---------- ---------- ---------- ---------- ----------
-class wxIpAddrCtrl( wxMaskedTextCtrl ):
+class wxIpAddrCtrlAccessorsMixin:
+ # Define wxIpAddrCtrl's list of attributes having their own
+ # Get/Set functions, exposing only those that make sense for
+ # an IP address control.
+
+ exposed_basectrl_params = (
+ 'fields',
+ 'retainFieldValidation',
+ 'formatcodes',
+ 'fillChar',
+ 'defaultValue',
+ 'description',
+
+ 'useFixedWidthFont',
+ 'signedForegroundColour',
+ 'emptyBackgroundColour',
+ 'validBackgroundColour',
+ 'invalidBackgroundColour',
+
+ 'emptyInvalid',
+ 'validFunc',
+ 'validRequired',
+ )
+
+ for param in exposed_basectrl_params:
+ propname = param[0].upper() + param[1:]
+ exec('def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param))
+ exec('def Get%s(self): return self.GetCtrlParameter("%s")''' % (propname, param))
+
+ if param.find('Colour') != -1:
+ # add non-british spellings, for backward-compatibility
+ propname.replace('Colour', 'Color')
+
+ exec('def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param))
+ exec('def Get%s(self): return self.GetCtrlParameter("%s")''' % (propname, param))
+
+
+
+class wxIpAddrCtrl( wxBaseMaskedTextCtrl, wxIpAddrCtrlAccessorsMixin ):
"""
This class is a particular type of wxMaskedTextCtrl that accepts
and understands the semantics of IP addresses, reformats input
as you move from field to field, and accepts '.' as a navigation
character, so that typing an IP address can be done naturally.
"""
+
+
+
def __init__( self, parent, id=-1, value = '',
pos = wxDefaultPosition,
size = wxDefaultSize,
@@ -6486,7 +6676,7 @@ class wxIpAddrCtrl( wxMaskedTextCtrl ):
kwargs['validRegex'] = "( \d| \d\d|(1\d\d|2[0-4]\d|25[0-5]))(\.( \d| \d\d|(1\d\d|2[0-4]\d|25[0-5]))){3}"
- wxMaskedTextCtrl.__init__(
+ wxBaseMaskedTextCtrl.__init__(
self, parent, id=id, value = value,
pos=pos, size=size,
style = style,
@@ -6495,6 +6685,7 @@ class wxIpAddrCtrl( wxMaskedTextCtrl ):
setupEventHandling = setupEventHandling,
**kwargs)
+
# set up individual field parameters as well:
field_params = {}
field_params['validRegex'] = "( | \d| \d |\d | \d\d|\d\d |\d \d|(1\d\d|2[0-4]\d|25[0-5]))"
@@ -6513,7 +6704,7 @@ class wxIpAddrCtrl( wxMaskedTextCtrl ):
def OnDot(self, event):
- dbg('wxIpAddrCtrl::OnDot', indent=1)
+## dbg('wxIpAddrCtrl::OnDot', indent=1)
pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
oldvalue = self.GetValue()
edit_start, edit_end, slice = self._FindFieldExtent(pos, getslice=True)
@@ -6524,26 +6715,26 @@ class wxIpAddrCtrl( wxMaskedTextCtrl ):
newvalue = oldvalue[:pos] + ' ' * (edit_end - pos) + oldvalue[edit_end:]
self._SetValue(newvalue)
self._SetInsertionPoint(pos)
- dbg(indent=0)
+## dbg(indent=0)
return self._OnChangeField(event)
def GetAddress(self):
- value = wxMaskedTextCtrl.GetValue(self)
+ value = wxBaseMaskedTextCtrl.GetValue(self)
return value.replace(' ','') # remove spaces from the value
def _OnCtrl_S(self, event):
- dbg("wxIpAddrCtrl::_OnCtrl_S")
+## dbg("wxIpAddrCtrl::_OnCtrl_S")
if self._demo:
print "value:", self.GetAddress()
return False
def SetValue(self, value):
- dbg('wxIpAddrCtrl::SetValue(%s)' % str(value), indent=1)
+## dbg('wxIpAddrCtrl::SetValue(%s)' % str(value), indent=1)
if type(value) not in (types.StringType, types.UnicodeType):
- dbg(indent=0)
+## dbg(indent=0)
raise ValueError('%s must be a string', str(value))
bValid = True # assume true
@@ -6573,13 +6764,13 @@ class wxIpAddrCtrl( wxMaskedTextCtrl ):
parts[i] = ' ' # convert empty field to 3-char length
if not bValid:
- dbg(indent=0)
+## dbg(indent=0)
raise ValueError('value (%s) must be a string of form n.n.n.n where n is empty or in range 0-255' % str(value))
else:
- dbg('parts:', parts)
+## dbg('parts:', parts)
value = string.join(parts, '.')
- wxMaskedTextCtrl.SetValue(self, value)
- dbg(indent=0)
+ wxBaseMaskedTextCtrl.SetValue(self, value)
+## dbg(indent=0)
## ---------- ---------- ---------- ---------- ---------- ---------- ----------
@@ -6893,7 +7084,7 @@ To see a great example of validations in action, try entering a bad email addres
("US Date + Time","USDATETIMEMMDDYYYY/HHMM"),
("US Date MMDDYYYY","USDATEMMDDYYYY/"),
("Time (with seconds)","TIMEHHMMSS"),
- ("Military Time\n(without seconds)","MILTIMEHHMM"),
+ ("Military Time\n(without seconds)","24HRTIMEHHMM"),
("Social Sec#","USSOCIALSEC"),
("Credit Card","CREDITCARD"),
("Expiration MM/YY","EXPDATEMMYY"),
@@ -6997,6 +7188,28 @@ i=1
## CHANGELOG:
## ====================
+## Version 1.5
+## (Reported) bugs fixed:
+## 1. Crash ensues if you attempt to change the mask of a read-only
+## wxMaskedComboBox after initial construction.
+## 2. Changed strategy of defining Get/Set property functions so that
+## these are now generated dynamically at runtime, rather than as
+## part of the class definition. (This makes it possible to have
+## more general base classes that have many more options for configuration
+## without requiring that derivations support the same options.)
+## 3. Fixed IsModified for _Paste() and _OnErase().
+##
+## Enhancements:
+## 1. Fixed "attribute function inheritance," since base control is more
+## generic than subsequent derivations, not all property functions of a
+## generic control should be exposed in those derivations. New strategy
+## uses base control classes (eg. wxBaseMaskedTextCtrl) that should be
+## used to derive new class types, and mixed with their own mixins to
+## only expose those attributes from the generic masked controls that
+## make sense for the derivation. (This makes Boa happier.)
+## 2. Renamed (with b-c) MILTIME autoformats to 24HRTIME, so as to be less
+## "parochial."
+##
## Version 1.4
## (Reported) bugs fixed:
## 1. Right-click menu allowed "cut" operation that destroyed mask
diff --git a/wxPython/wxPython/lib/maskednumctrl.py b/wxPython/wxPython/lib/maskednumctrl.py
index 2e1acfece2..6a1b069369 100644
--- a/wxPython/wxPython/lib/maskednumctrl.py
+++ b/wxPython/wxPython/lib/maskednumctrl.py
@@ -29,7 +29,7 @@
# are exceeded.
#
# wxMaskedNumCtrl is intended to support fixed-point numeric entry, and
-# is derived from wxMaskedTextCtrl. As such, it supports a limited range
+# is derived from wxBaseMaskedTextCtrl. As such, it supports a limited range
# of values to comply with a fixed-width entry mask.
"""
@@ -74,6 +74,7 @@ Here's the API:
emptyBackgroundColour = "White",
validBackgroundColour = "White",
invalidBackgroundColour = "Yellow",
+ autoSize = True
)
@@ -164,6 +165,15 @@ Here's the API:
- invalidBackgroundColour
- Color value used for illegal values or values out-of-bounds of the
control when the bounds are set but the control is not limited.
+
+ - autoSize
+
- Boolean indicating whether or not the control should set its own
+ width based on the integer and fraction widths. True by default.
+ Note: Setting this to False will produce seemingly odd
+ behavior unless the control is large enough to hold the maximum
+ specified value given the widths and the sign positions; if not,
+ the control will appear to "jump around" as the contents scroll.
+ (ie. autoSize is highly recommended.)
@@ -340,6 +350,12 @@ within the control. (The default is True.)
the field values on entry.
+SetAutoSize(bool)
+Resets the autoSize attribute of the control.
+GetAutoSize()
+Returns the current state of the autoSize attribute for the control.
+
+
"""
@@ -351,7 +367,7 @@ MAXINT = maxint # (constants should be in upper case)
MININT = -maxint-1
from wxPython.tools.dbg import Logger
-from wxPython.lib.maskededit import wxMaskedEditMixin, wxMaskedTextCtrl, Field
+from wxPython.lib.maskededit import wxMaskedEditMixin, wxBaseMaskedTextCtrl, Field
import wxPython.utils
dbg = Logger()
dbg(enable=0)
@@ -381,16 +397,54 @@ class wxMaskedNumNumberUpdatedEvent(wxPyCommandEvent):
#----------------------------------------------------------------------------
+class wxMaskedNumCtrlAccessorsMixin:
+ # Define wxMaskedNumCtrl's list of attributes having their own
+ # Get/Set functions, ignoring those that make no sense for
+ # an numeric control.
+ exposed_basectrl_params = (
+ 'decimalChar',
+ 'shiftDecimalChar',
+ 'groupChar',
+ 'useParensForNegatives',
+ 'defaultValue',
+ 'description',
-class wxMaskedNumCtrl(wxMaskedTextCtrl):
+ 'useFixedWidthFont',
+ 'autoSize',
+ 'signedForegroundColour',
+ 'emptyBackgroundColour',
+ 'validBackgroundColour',
+ 'invalidBackgroundColour',
+
+ 'emptyInvalid',
+ 'validFunc',
+ 'validRequired',
+ )
+ for param in exposed_basectrl_params:
+ propname = param[0].upper() + param[1:]
+ exec('def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param))
+ exec('def Get%s(self): return self.GetCtrlParameter("%s")''' % (propname, param))
+
+ if param.find('Colour') != -1:
+ # add non-british spellings, for backward-compatibility
+ propname.replace('Colour', 'Color')
+
+ exec('def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param))
+ exec('def Get%s(self): return self.GetCtrlParameter("%s")''' % (propname, param))
+
+
+
+#----------------------------------------------------------------------------
+
+class wxMaskedNumCtrl(wxBaseMaskedTextCtrl, wxMaskedNumCtrlAccessorsMixin):
valid_ctrl_params = {
'integerWidth': 10, # by default allow all 32-bit integers
- 'fractionWidth': 0, # by default, use integers
+ 'fractionWidth': 0, # by default, use integers
'decimalChar': '.', # by default, use '.' for decimal point
'allowNegative': True, # by default, allow negative numbers
'useParensForNegatives': False, # by default, use '-' to indicate negatives
- 'groupDigits': True, # by default, don't insert grouping
+ 'groupDigits': True, # by default, don't insert grouping
'groupChar': ',', # by default, use ',' for grouping
'min': None, # by default, no bounds set
'max': None,
@@ -402,7 +456,8 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
'emptyBackgroundColour': "White",
'validBackgroundColour': "White",
'invalidBackgroundColour': "Yellow",
- 'useFixedWidthFont': True, # by default, use a fixed-width font
+ 'useFixedWidthFont': True, # by default, use a fixed-width font
+ 'autoSize': True, # by default, set the width of the control based on the mask
}
@@ -475,6 +530,12 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
del init_args['integerWidth']
del init_args['fractionWidth']
+ self._autoSize = init_args['autoSize']
+ if self._autoSize:
+ formatcodes = 'FR<'
+ else:
+ formatcodes = 'R<'
+
mask = intmask+fracmask
@@ -484,11 +545,11 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
self._typedSign = False
# Construct the base control:
- wxMaskedTextCtrl.__init__(
+ wxBaseMaskedTextCtrl.__init__(
self, parent, id, '',
pos, size, style, validator, name,
mask = mask,
- formatcodes = 'FR<',
+ formatcodes = formatcodes,
fields = fields,
validFunc=self.IsInBounds,
setupEventHandling = False)
@@ -525,7 +586,8 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
if( (kwargs.has_key('integerWidth') and kwargs['integerWidth'] != self._integerWidth)
or (kwargs.has_key('fractionWidth') and kwargs['fractionWidth'] != self._fractionWidth)
- or (kwargs.has_key('groupDigits') and kwargs['groupDigits'] != self._groupDigits) ):
+ or (kwargs.has_key('groupDigits') and kwargs['groupDigits'] != self._groupDigits)
+ or (kwargs.has_key('autoSize') and kwargs['autoSize'] != self._autoSize) ):
fields = {}
@@ -601,7 +663,7 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
dbg('kwargs:', kwargs)
# reprocess existing format codes to ensure proper resulting format:
- formatcodes = self.GetFormatcodes()
+ formatcodes = self.GetCtrlParameter('formatcodes')
if kwargs.has_key('allowNegative'):
if kwargs['allowNegative'] and '-' not in formatcodes:
formatcodes += '-'
@@ -628,6 +690,16 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
formatcodes = formatcodes.replace('S','')
maskededit_kwargs['formatcodes'] = formatcodes
+ if kwargs.has_key('autoSize'):
+ self._autoSize = kwargs['autoSize']
+ if kwargs['autoSize'] and 'F' not in formatcodes:
+ formatcodes += 'F'
+ maskededit_kwargs['formatcodes'] = formatcodes
+ elif not kwargs['autoSize'] and 'F' in formatcodes:
+ formatcodes = formatcodes.replace('F', '')
+ maskededit_kwargs['formatcodes'] = formatcodes
+
+
if 'r' in formatcodes and self._fractionWidth:
# top-level mask should only be right insert if no fractional
# part will be shown; ie. if reconfiguring control, remove
@@ -635,6 +707,7 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
formatcodes = formatcodes.replace('r', '')
maskededit_kwargs['formatcodes'] = formatcodes
+
if kwargs.has_key('limited'):
if kwargs['limited'] and not self._limited:
maskededit_kwargs['validRequired'] = True
@@ -720,7 +793,7 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
dbg('abs(value):', value)
self._isNeg = False
- elif not self._allowNone and wxMaskedTextCtrl.GetValue(self) == '':
+ elif not self._allowNone and wxBaseMaskedTextCtrl.GetValue(self) == '':
if self._min > 0:
value = self._min
else:
@@ -762,7 +835,7 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
else:
fracstart, fracend = self._fields[1]._extent
if candidate is None:
- value = self._toGUI(wxMaskedTextCtrl.GetValue(self))
+ value = self._toGUI(wxBaseMaskedTextCtrl.GetValue(self))
else:
value = self._toGUI(candidate)
fracstring = value[fracstart:fracend].strip()
@@ -811,8 +884,8 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
if numvalue == "":
if self._allowNone:
- dbg('calling base wxMaskedTextCtrl._SetValue(self, "%s")' % value)
- wxMaskedTextCtrl._SetValue(self, value)
+ dbg('calling base wxBaseMaskedTextCtrl._SetValue(self, "%s")' % value)
+ wxBaseMaskedTextCtrl._SetValue(self, value)
self.Refresh()
return
elif self._min > 0 and self.IsLimited():
@@ -912,7 +985,7 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
# reasonable instead:
dbg('setting replacement value:', replacement)
self._SetValue(self._toGUI(replacement))
- sel_start = wxMaskedTextCtrl.GetValue(self).find(str(abs(replacement))) # find where it put the 1, so we can select it
+ sel_start = wxBaseMaskedTextCtrl.GetValue(self).find(str(abs(replacement))) # find where it put the 1, so we can select it
sel_to = sel_start + len(str(abs(replacement)))
dbg('queuing selection of (%d, %d)' %(sel_start, sel_to))
wxCallAfter(self.SetInsertionPoint, sel_start)
@@ -938,8 +1011,8 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
sel_start, sel_to = self._GetSelection() # record current insertion point
- dbg('calling base wxMaskedTextCtrl._SetValue(self, "%s")' % adjvalue)
- wxMaskedTextCtrl._SetValue(self, adjvalue)
+ dbg('calling base wxBaseMaskedTextCtrl._SetValue(self, "%s")' % adjvalue)
+ wxBaseMaskedTextCtrl._SetValue(self, adjvalue)
# After all actions so far scheduled, check that resulting cursor
# position is appropriate, and move if not:
wxCallAfter(self._CheckInsertionPoint)
@@ -972,7 +1045,7 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
# delete next digit to appropriate side:
if self._groupDigits:
key = event.GetKeyCode()
- value = wxMaskedTextCtrl.GetValue(self)
+ value = wxBaseMaskedTextCtrl.GetValue(self)
sel_start, sel_to = self._GetSelection()
if key == WXK_BACK:
@@ -998,7 +1071,7 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
self.SetInsertionPoint(sel_start)
self.SetSelection(sel_start, sel_to+1)
- wxMaskedTextCtrl._OnErase(self, event)
+ wxBaseMaskedTextCtrl._OnErase(self, event)
dbg(indent=0)
@@ -1012,7 +1085,7 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
before passing the events on.
"""
dbg('wxMaskedNumCtrl::OnTextChange', indent=1)
- if not wxMaskedTextCtrl._OnTextChange(self, event):
+ if not wxBaseMaskedTextCtrl._OnTextChange(self, event):
dbg(indent=0)
return
@@ -1033,7 +1106,7 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
def _GetValue(self):
"""
- Override of wxMaskedTextCtrl to allow amixin to get the raw text value of the
+ Override of wxBaseMaskedTextCtrl to allow mixin to get the raw text value of the
control with this function.
"""
return wxTextCtrl.GetValue(self)
@@ -1043,7 +1116,7 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
"""
Returns the current numeric value of the control.
"""
- return self._fromGUI( wxMaskedTextCtrl.GetValue(self) )
+ return self._fromGUI( wxBaseMaskedTextCtrl.GetValue(self) )
def SetValue(self, value):
"""
@@ -1054,16 +1127,16 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
A ValueError exception will be raised if an invalid value
is specified.
"""
- wxMaskedTextCtrl.SetValue( self, self._toGUI(value) )
+ wxBaseMaskedTextCtrl.SetValue( self, self._toGUI(value) )
def SetIntegerWidth(self, value):
- self.SetCtrlParameters(integerWidth=value)
+ self.SetParameters(integerWidth=value)
def GetIntegerWidth(self):
return self._integerWidth
def SetFractionWidth(self, value):
- self.SetCtrlParameters(fractionWidth=value)
+ self.SetParameters(fractionWidth=value)
def GetFractionWidth(self):
return self._fractionWidth
@@ -1208,7 +1281,7 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
except ValueError, e:
dbg('error getting NumValue(self._toGUI(value)):', e, indent=0)
return False
- if value == '':
+ if value.strip() == '':
value = None
elif self._fractionWidth:
value = float(value)
@@ -1281,6 +1354,12 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
def GetSelectOnEntry(self):
return self._selectOnEntry
+ def SetAutoSize(self, value):
+ self.SetParameters(autoSize=value)
+ def GetAutoSize(self):
+ return self._autoSize
+
+
# (Other parameter accessors are inherited from base class)
@@ -1298,6 +1377,14 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
elif type(value) in (types.StringType, types.UnicodeType):
value = self._GetNumValue(value)
dbg('cleansed num value: "%s"' % value)
+ if value == "":
+ if self.IsNoneAllowed():
+ dbg(indent=0)
+ return self._template
+ else:
+ dbg('exception raised:', e, indent=0)
+ raise ValueError ('wxMaskedNumCtrl requires numeric value, passed %s'% repr(value) )
+ # else...
try:
if self._fractionWidth or value.find('.') != -1:
value = float(value)
@@ -1367,7 +1454,7 @@ class wxMaskedNumCtrl(wxMaskedTextCtrl):
# So, to ensure consistency and to prevent spurious ValueErrors,
# we make the following test, and react accordingly:
#
- if value == '':
+ if value.strip() == '':
if not self.IsNoneAllowed():
dbg('empty value; not allowed,returning 0', indent = 0)
if self._fractionWidth:
@@ -1503,3 +1590,12 @@ i=0
## =============================##
## 1. Add support for printf-style format specification.
## 2. Add option for repositioning on 'illegal' insertion point.
+##
+## Version 1.1
+## 1. Fixed .SetIntegerWidth() and .SetFractionWidth() functions.
+## 2. Added autoSize parameter, to allow manual sizing of the control.
+## 3. Changed inheritance to use wxBaseMaskedTextCtrl, to remove exposure of
+## nonsensical parameter methods from the control, so it will work
+## properly with Boa.
+## 4. Fixed allowNone bug found by user sameerc1@grandecom.net
+
diff --git a/wxPython/wxPython/lib/timectrl.py b/wxPython/wxPython/lib/timectrl.py
index 85a9dd9b30..bbc34b3612 100644
--- a/wxPython/wxPython/lib/timectrl.py
+++ b/wxPython/wxPython/lib/timectrl.py
@@ -53,7 +53,9 @@ Here's the API for wxTimeCtrl:
style = wxTE_PROCESS_TAB,
validator = wxDefaultValidator,
name = "time",
+ format = 'HHMMSS',
fmt24hr = False,
+ displaySeconds = True,
spinButton = None,
min = None,
max = None,
@@ -77,10 +79,21 @@ Here's the API for wxTimeCtrl:
of its validation for entry control is handled internally. However, a validator
can be supplied to provide data transfer capability to the control.
+ format
+ 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.
fmt24hr
If True, control will display time in 24 hour time format; if False, it will
use 12 hour AM/PM format. SetValue() will adjust values accordingly for the
- control, based on the format specified.
+ control, based on the format specified. (This value is ignored if the format
+ parameter is specified.)
+
+ displaySeconds
+ If True, control will include a seconds field; if False, it will
+ just show hours and minutes. (This value is ignored if the format
+ parameter is specified.)
spinButton
If specified, this button's events will be bound to the behavior of the
@@ -242,7 +255,7 @@ value to fall within the current bounds.
import string, copy
from wxPython.wx import *
from wxPython.tools.dbg import Logger
-from wxPython.lib.maskededit import wxMaskedTextCtrl, Field
+from wxPython.lib.maskededit import wxBaseMaskedTextCtrl, Field
import wxPython.utils
dbg = Logger()
dbg(enable=0)
@@ -267,11 +280,41 @@ class TimeUpdatedEvent(wxPyCommandEvent):
"""Retrieve the value of the time control at the time this event was generated"""
return self.value
+class wxTimeCtrlAccessorsMixin:
+ # Define wxMaskedNumCtrl's list of attributes having their own
+ # Get/Set functions, ignoring those that make no sense for
+ # an numeric control.
+ exposed_basectrl_params = (
+ 'defaultValue',
+ 'description',
-class wxTimeCtrl(wxMaskedTextCtrl):
+ 'useFixedWidthFont',
+ 'emptyBackgroundColour',
+ 'validBackgroundColour',
+ 'invalidBackgroundColour',
+
+ 'validFunc',
+ 'validRequired',
+ )
+ for param in exposed_basectrl_params:
+ propname = param[0].upper() + param[1:]
+ exec('def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param))
+ exec('def Get%s(self): return self.GetCtrlParameter("%s")''' % (propname, param))
+
+ if param.find('Colour') != -1:
+ # add non-british spellings, for backward-compatibility
+ propname.replace('Colour', 'Color')
+
+ exec('def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param))
+ exec('def Get%s(self): return self.GetCtrlParameter("%s")''' % (propname, param))
+
+
+
+class wxTimeCtrl(wxBaseMaskedTextCtrl):
valid_ctrl_params = {
- 'display_seconds' : True, # by default, shows seconds
+ 'format' : 'HHMMSS', # default format code
+ 'displaySeconds' : True, # by default, shows seconds
'min': None, # by default, no bounds set
'max': None,
'limited': False, # by default, no limiting even if bounds set
@@ -303,60 +346,39 @@ class wxTimeCtrl(wxMaskedTextCtrl):
limited = self.__limited
self.__posCurrent = 0
+ # handle deprecated keword argument name:
+ if kwargs.has_key('display_seconds'):
+ kwargs['displaySeconds'] = kwargs['display_seconds']
+ del kwargs['display_seconds']
+ if not kwargs.has_key('displaySeconds'):
+ kwargs['displaySeconds'] = True
- # (handle positional args (from original release) differently from rest of kwargs:)
- self.__fmt24hr = fmt24hr
+ # (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']:
+ kwargs['format'] = '24HHMMSS'
+ del kwargs['displaySeconds']
+ else:
+ kwargs['format'] = '24HHMM'
+ else:
+ if kwargs.has_key('displaySeconds') and kwargs['displaySeconds']:
+ kwargs['format'] = 'HHMMSS'
+ del kwargs['displaySeconds']
+ else:
+ kwargs['format'] = 'HHMM'
- maskededit_kwargs = {}
+ if not kwargs.has_key('useFixedWidthFont'):
+ # allow control over font selection:
+ kwargs['useFixedWidthFont'] = self.__useFixedWidthFont
- # assign keyword args as appropriate:
- for key, param_value in kwargs.items():
- if key not in wxTimeCtrl.valid_ctrl_params.keys():
- raise AttributeError('invalid keyword argument "%s"' % key)
+ maskededit_kwargs = self.SetParameters(**kwargs)
- if key == "display_seconds":
- self.__display_seconds = param_value
-
- elif key == "min": min = param_value
- elif key == "max": max = param_value
- elif key == "limited": limited = param_value
-
- elif key == "useFixedWidthFont":
- maskededit_kwargs[key] = param_value
- elif key == "oob_color":
- maskededit_kwargs['invalidBackgroundColor'] = param_value
-
- if self.__fmt24hr:
- if self.__display_seconds: maskededit_kwargs['autoformat'] = 'MILTIMEHHMMSS'
- else: maskededit_kwargs['autoformat'] = 'MILTIMEHHMM'
-
- # Set hour field to zero-pad, right-insert, require explicit field change,
- # select entire field on entry, and require a resultant valid entry
- # to allow character entry:
- hourfield = Field(formatcodes='0r