MaskedEditCtrl updates from Will Sadkin
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@26096 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
parent
64ff2615ae
commit
fffd96b769
@ -4,6 +4,7 @@ import sys
|
||||
import traceback
|
||||
|
||||
import wx
|
||||
import wx.lib.maskededit as me
|
||||
import wx.lib.maskednumctrl as mnum
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
@ -31,13 +32,13 @@ The controls at the top reconfigure the resulting control at the bottom.
|
||||
)
|
||||
|
||||
groupcharlabel = wx.StaticText( panel,-1, "Grouping char:" )
|
||||
self.groupchar = mnum.MaskedTextCtrl(
|
||||
self.groupchar = me.MaskedTextCtrl(
|
||||
panel, -1, value=',', mask='&', excludeChars = '-()',
|
||||
formatcodes='F', emptyInvalid=True, validRequired=True
|
||||
)
|
||||
|
||||
decimalcharlabel = wx.StaticText( panel,-1, "Decimal char:" )
|
||||
self.decimalchar = mnum.MaskedTextCtrl(
|
||||
self.decimalchar = me.MaskedTextCtrl(
|
||||
panel, -1, value='.', mask='&', excludeChars = '-()',
|
||||
formatcodes='F', emptyInvalid=True, validRequired=True
|
||||
)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@
|
||||
# Created: 09/06/2003
|
||||
# Copyright: (c) 2003 by Will Sadkin
|
||||
# RCS-ID: $Id$
|
||||
# License: wxWindows license
|
||||
# License: wxWidgets license
|
||||
#----------------------------------------------------------------------------
|
||||
# NOTE:
|
||||
# This was written to provide a numeric edit control for wxPython that
|
||||
@ -29,7 +29,7 @@
|
||||
# are exceeded.
|
||||
#
|
||||
# MaskedNumCtrl is intended to support fixed-point numeric entry, and
|
||||
# is derived from MaskedTextCtrl. As such, it supports a limited range
|
||||
# is derived from BaseMaskedTextCtrl. As such, it supports a limited range
|
||||
# of values to comply with a fixed-width entry mask.
|
||||
#----------------------------------------------------------------------------
|
||||
# 12/09/2003 - Jeff Grimmett (grimmtooth@softhome.net)
|
||||
@ -65,10 +65,10 @@ Here's the API:
|
||||
<B>MaskedNumCtrl</B>(
|
||||
parent, id = -1,
|
||||
<B>value</B> = 0,
|
||||
pos = wxDefaultPosition,
|
||||
size = wxDefaultSize,
|
||||
pos = wx.DefaultPosition,
|
||||
size = wx.DefaultSize,
|
||||
style = 0,
|
||||
validator = wxDefaultValidator,
|
||||
validator = wx.DefaultValidator,
|
||||
name = "maskednumber",
|
||||
<B>integerWidth</B> = 10,
|
||||
<B>fractionWidth</B> = 0,
|
||||
@ -87,6 +87,7 @@ Here's the API:
|
||||
<B>emptyBackgroundColour</B> = "White",
|
||||
<B>validBackgroundColour</B> = "White",
|
||||
<B>invalidBackgroundColour</B> = "Yellow",
|
||||
<B>autoSize</B> = True
|
||||
)
|
||||
</PRE>
|
||||
<UL>
|
||||
@ -177,11 +178,20 @@ Here's the API:
|
||||
<DT><B>invalidBackgroundColour</B>
|
||||
<DD>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.
|
||||
<BR>
|
||||
<DT><B>autoSize</B>
|
||||
<DD>Boolean indicating whether or not the control should set its own
|
||||
width based on the integer and fraction widths. True by default.
|
||||
<B><I>Note:</I></B> 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.)
|
||||
</UL>
|
||||
<BR>
|
||||
<BR>
|
||||
<DT><B>EVT_MASKEDNUM(win, id, func)</B>
|
||||
<DD>Respond to a wxEVT_COMMAND_MASKED_NUMBER_UPDATED event, generated when
|
||||
<DD>Respond to a EVT_COMMAND_MASKED_NUMBER_UPDATED event, generated when
|
||||
the value changes. Notice that this event will always be sent when the
|
||||
control's contents changes - whether this is due to user input or
|
||||
comes from the program itself (for example, if SetValue() is called.)
|
||||
@ -353,6 +363,12 @@ within the control. (The default is True.)
|
||||
the field values on entry.
|
||||
<BR>
|
||||
<BR>
|
||||
<DT><B>SetAutoSize(bool)</B>
|
||||
<DD>Resets the autoSize attribute of the control.
|
||||
<DT><B>GetAutoSize()</B>
|
||||
<DD>Returns the current state of the autoSize attribute for the control.
|
||||
<BR>
|
||||
<BR>
|
||||
</DL>
|
||||
</body></html>
|
||||
"""
|
||||
@ -368,8 +384,7 @@ MAXINT = maxint # (constants should be in upper case)
|
||||
MININT = -maxint-1
|
||||
|
||||
from wx.tools.dbg import Logger
|
||||
from wx.lib.maskededit import MaskedEditMixin, MaskedTextCtrl, Field
|
||||
|
||||
from wx.lib.maskededit import MaskedEditMixin, BaseMaskedTextCtrl, Field
|
||||
dbg = Logger()
|
||||
dbg(enable=0)
|
||||
|
||||
@ -394,8 +409,47 @@ class MaskedNumNumberUpdatedEvent(wx.PyCommandEvent):
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
class MaskedNumCtrlAccessorsMixin:
|
||||
# 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',
|
||||
|
||||
'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 MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
||||
|
||||
class MaskedNumCtrl(MaskedTextCtrl):
|
||||
|
||||
valid_ctrl_params = {
|
||||
'integerWidth': 10, # by default allow all 32-bit integers
|
||||
@ -416,6 +470,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
'validBackgroundColour': "White",
|
||||
'invalidBackgroundColour': "Yellow",
|
||||
'useFixedWidthFont': True, # by default, use a fixed-width font
|
||||
'autoSize': True, # by default, set the width of the control based on the mask
|
||||
}
|
||||
|
||||
|
||||
@ -488,6 +543,12 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
del init_args['integerWidth']
|
||||
del init_args['fractionWidth']
|
||||
|
||||
self._autoSize = init_args['autoSize']
|
||||
if self._autoSize:
|
||||
formatcodes = 'FR<'
|
||||
else:
|
||||
formatcodes = 'R<'
|
||||
|
||||
|
||||
mask = intmask+fracmask
|
||||
|
||||
@ -497,11 +558,11 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
self._typedSign = False
|
||||
|
||||
# Construct the base control:
|
||||
MaskedTextCtrl.__init__(
|
||||
BaseMaskedTextCtrl.__init__(
|
||||
self, parent, id, '',
|
||||
pos, size, style, validator, name,
|
||||
mask = mask,
|
||||
formatcodes = 'FR<',
|
||||
formatcodes = formatcodes,
|
||||
fields = fields,
|
||||
validFunc=self.IsInBounds,
|
||||
setupEventHandling = False)
|
||||
@ -538,7 +599,8 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
|
||||
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 = {}
|
||||
|
||||
@ -614,7 +676,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
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 += '-'
|
||||
@ -641,6 +703,16 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
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
|
||||
@ -648,6 +720,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
formatcodes = formatcodes.replace('r', '')
|
||||
maskededit_kwargs['formatcodes'] = formatcodes
|
||||
|
||||
|
||||
if kwargs.has_key('limited'):
|
||||
if kwargs['limited'] and not self._limited:
|
||||
maskededit_kwargs['validRequired'] = True
|
||||
@ -661,6 +734,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
|
||||
# Record end of integer and place cursor there:
|
||||
integerEnd = self._fields[0]._extent[1]
|
||||
self.SetInsertionPoint(0)
|
||||
self.SetInsertionPoint(integerEnd)
|
||||
self.SetSelection(integerEnd, integerEnd)
|
||||
|
||||
@ -733,7 +807,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
dbg('abs(value):', value)
|
||||
self._isNeg = False
|
||||
|
||||
elif not self._allowNone and MaskedTextCtrl.GetValue(self) == '':
|
||||
elif not self._allowNone and BaseMaskedTextCtrl.GetValue(self) == '':
|
||||
if self._min > 0:
|
||||
value = self._min
|
||||
else:
|
||||
@ -775,7 +849,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
else:
|
||||
fracstart, fracend = self._fields[1]._extent
|
||||
if candidate is None:
|
||||
value = self._toGUI(MaskedTextCtrl.GetValue(self))
|
||||
value = self._toGUI(BaseMaskedTextCtrl.GetValue(self))
|
||||
else:
|
||||
value = self._toGUI(candidate)
|
||||
fracstring = value[fracstart:fracend].strip()
|
||||
@ -824,8 +898,8 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
|
||||
if numvalue == "":
|
||||
if self._allowNone:
|
||||
dbg('calling base MaskedTextCtrl._SetValue(self, "%s")' % value)
|
||||
MaskedTextCtrl._SetValue(self, value)
|
||||
dbg('calling base BaseMaskedTextCtrl._SetValue(self, "%s")' % value)
|
||||
BaseMaskedTextCtrl._SetValue(self, value)
|
||||
self.Refresh()
|
||||
return
|
||||
elif self._min > 0 and self.IsLimited():
|
||||
@ -925,7 +999,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
# reasonable instead:
|
||||
dbg('setting replacement value:', replacement)
|
||||
self._SetValue(self._toGUI(replacement))
|
||||
sel_start = MaskedTextCtrl.GetValue(self).find(str(abs(replacement))) # find where it put the 1, so we can select it
|
||||
sel_start = BaseMaskedTextCtrl.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))
|
||||
wx.CallAfter(self.SetInsertionPoint, sel_start)
|
||||
@ -951,8 +1025,8 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
|
||||
|
||||
sel_start, sel_to = self._GetSelection() # record current insertion point
|
||||
dbg('calling base MaskedTextCtrl._SetValue(self, "%s")' % adjvalue)
|
||||
MaskedTextCtrl._SetValue(self, adjvalue)
|
||||
dbg('calling BaseMaskedTextCtrl._SetValue(self, "%s")' % adjvalue)
|
||||
BaseMaskedTextCtrl._SetValue(self, adjvalue)
|
||||
# After all actions so far scheduled, check that resulting cursor
|
||||
# position is appropriate, and move if not:
|
||||
wx.CallAfter(self._CheckInsertionPoint)
|
||||
@ -985,7 +1059,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
# delete next digit to appropriate side:
|
||||
if self._groupDigits:
|
||||
key = event.GetKeyCode()
|
||||
value = MaskedTextCtrl.GetValue(self)
|
||||
value = BaseMaskedTextCtrl.GetValue(self)
|
||||
sel_start, sel_to = self._GetSelection()
|
||||
|
||||
if key == wx.WXK_BACK:
|
||||
@ -1011,7 +1085,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
self.SetInsertionPoint(sel_start)
|
||||
self.SetSelection(sel_start, sel_to+1)
|
||||
|
||||
MaskedTextCtrl._OnErase(self, event)
|
||||
BaseMaskedTextCtrl._OnErase(self, event)
|
||||
dbg(indent=0)
|
||||
|
||||
|
||||
@ -1025,7 +1099,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
before passing the events on.
|
||||
"""
|
||||
dbg('MaskedNumCtrl::OnTextChange', indent=1)
|
||||
if not MaskedTextCtrl._OnTextChange(self, event):
|
||||
if not BaseMaskedTextCtrl._OnTextChange(self, event):
|
||||
dbg(indent=0)
|
||||
return
|
||||
|
||||
@ -1046,7 +1120,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
|
||||
def _GetValue(self):
|
||||
"""
|
||||
Override of MaskedTextCtrl to allow amixin to get the raw text value of the
|
||||
Override of BaseMaskedTextCtrl to allow mixin to get the raw text value of the
|
||||
control with this function.
|
||||
"""
|
||||
return wx.TextCtrl.GetValue(self)
|
||||
@ -1056,7 +1130,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
"""
|
||||
Returns the current numeric value of the control.
|
||||
"""
|
||||
return self._fromGUI( MaskedTextCtrl.GetValue(self) )
|
||||
return self._fromGUI( BaseMaskedTextCtrl.GetValue(self) )
|
||||
|
||||
def SetValue(self, value):
|
||||
"""
|
||||
@ -1067,16 +1141,16 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
A ValueError exception will be raised if an invalid value
|
||||
is specified.
|
||||
"""
|
||||
MaskedTextCtrl.SetValue( self, self._toGUI(value) )
|
||||
BaseMaskedTextCtrl.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
|
||||
|
||||
@ -1221,7 +1295,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
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)
|
||||
@ -1294,6 +1368,12 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
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)
|
||||
|
||||
|
||||
@ -1311,6 +1391,14 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
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)
|
||||
@ -1380,7 +1468,7 @@ class MaskedNumCtrl(MaskedTextCtrl):
|
||||
# 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:
|
||||
@ -1514,3 +1602,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
|
||||
|
||||
|
@ -60,12 +60,14 @@ Here's the API for TimeCtrl:
|
||||
<B>TimeCtrl</B>(
|
||||
parent, id = -1,
|
||||
<B>value</B> = '12:00:00 AM',
|
||||
pos = wxDefaultPosition,
|
||||
size = wxDefaultSize,
|
||||
pos = wx.DefaultPosition,
|
||||
size = wx.DefaultSize,
|
||||
<B>style</B> = wxTE_PROCESS_TAB,
|
||||
<B>validator</B> = wxDefaultValidator,
|
||||
<B>validator</B> = wx.DefaultValidator,
|
||||
name = "time",
|
||||
<B>format</B> = 'HHMMSS',
|
||||
<B>fmt24hr</B> = False,
|
||||
<B>displaySeconds</B> = True,
|
||||
<B>spinButton</B> = None,
|
||||
<B>min</B> = None,
|
||||
<B>max</B> = None,
|
||||
@ -80,7 +82,7 @@ Here's the API for TimeCtrl:
|
||||
with SetValue() after instantiation of the control.)
|
||||
<DL><B>size</B>
|
||||
<DD>The size of the control will be automatically adjusted for 12/24 hour format
|
||||
if wxDefaultSize is specified.
|
||||
if wx.DefaultSize is specified.
|
||||
<DT><B>style</B>
|
||||
<DD>By default, TimeCtrl will process TAB events, by allowing tab to the
|
||||
different cells within the control.
|
||||
@ -89,10 +91,22 @@ Here's the API for TimeCtrl:
|
||||
of its validation for entry control is handled internally. However, a validator
|
||||
can be supplied to provide data transfer capability to the control.
|
||||
<BR>
|
||||
<DT><B>format</B>
|
||||
<DD>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.
|
||||
<BR>
|
||||
<DT><B>fmt24hr</B>
|
||||
<DD>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 <i>format</i>
|
||||
parameter is specified.)
|
||||
<BR>
|
||||
<DT><B>displaySeconds</B>
|
||||
<DD>If True, control will include a seconds field; if False, it will
|
||||
just show hours and minutes. (This value is ignored if the <i>format</i>
|
||||
parameter is specified.)
|
||||
<BR>
|
||||
<DT><B>spinButton</B>
|
||||
<DD>If specified, this button's events will be bound to the behavior of the
|
||||
@ -151,7 +165,7 @@ set to Jan 1, 1970.) (Same as GetValue(as_wxDateTime=True); provided for backwar
|
||||
compatibility with previous release.)
|
||||
<BR>
|
||||
<BR>
|
||||
<DT><B>BindSpinButton(wxSpinBtton)</B>
|
||||
<DT><B>BindSpinButton(SpinBtton)</B>
|
||||
<DD>Binds an externally created spin button to the control, so that up/down spin
|
||||
events change the active cell or selection in the control (in addition to the
|
||||
up/down cursor keys.) (This is primarily to allow you to create a "standard"
|
||||
@ -258,7 +272,7 @@ import types
|
||||
import wx
|
||||
|
||||
from wx.tools.dbg import Logger
|
||||
from wx.lib.maskededit import MaskedTextCtrl, Field
|
||||
from wx.lib.maskededit import BaseMaskedTextCtrl, Field
|
||||
|
||||
dbg = Logger()
|
||||
dbg(enable=0)
|
||||
@ -281,11 +295,40 @@ class TimeUpdatedEvent(wx.PyCommandEvent):
|
||||
"""Retrieve the value of the time control at the time this event was generated"""
|
||||
return self.value
|
||||
|
||||
class TimeCtrlAccessorsMixin:
|
||||
# Define TimeCtrl'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 TimeCtrl(MaskedTextCtrl):
|
||||
'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 TimeCtrl(BaseMaskedTextCtrl):
|
||||
|
||||
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
|
||||
@ -316,61 +359,39 @@ class TimeCtrl(MaskedTextCtrl):
|
||||
max = self.__max
|
||||
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
|
||||
|
||||
maskededit_kwargs = {}
|
||||
|
||||
# assign keyword args as appropriate:
|
||||
for key, param_value in kwargs.items():
|
||||
if key not in TimeCtrl.valid_ctrl_params.keys():
|
||||
raise AttributeError('invalid keyword argument "%s"' % key)
|
||||
|
||||
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<SV', validRegex='0\d|1\d|2[0123]', validRequired=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']:
|
||||
kwargs['format'] = '24HHMMSS'
|
||||
del kwargs['displaySeconds']
|
||||
else:
|
||||
if self.__display_seconds: maskededit_kwargs['autoformat'] = 'TIMEHHMMSS'
|
||||
else: maskededit_kwargs['autoformat'] = 'TIMEHHMM'
|
||||
kwargs['format'] = '24HHMM'
|
||||
else:
|
||||
if kwargs.has_key('displaySeconds') and kwargs['displaySeconds']:
|
||||
kwargs['format'] = 'HHMMSS'
|
||||
del kwargs['displaySeconds']
|
||||
else:
|
||||
kwargs['format'] = 'HHMM'
|
||||
|
||||
# Set hour field to allow spaces (at start), right-insert,
|
||||
# require explicit field change, select entire field on entry,
|
||||
# and require a resultant valid entry to allow character entry:
|
||||
hourfield = Field(formatcodes='_0<rSV', validRegex='0[1-9]| [1-9]|1[012]', validRequired=True)
|
||||
ampmfield = Field(formatcodes='S', emptyInvalid = True, validRequired = True)
|
||||
if not kwargs.has_key('useFixedWidthFont'):
|
||||
# allow control over font selection:
|
||||
kwargs['useFixedWidthFont'] = self.__useFixedWidthFont
|
||||
|
||||
# Field 1 is always a zero-padded right-insert minute field,
|
||||
# similarly configured as above:
|
||||
minutefield = Field(formatcodes='0r<SV', validRegex='[0-5]\d', validRequired=True)
|
||||
maskededit_kwargs = self.SetParameters(**kwargs)
|
||||
|
||||
fields = [ hourfield, minutefield ]
|
||||
if self.__display_seconds:
|
||||
fields.append(copy.copy(minutefield)) # second field has same constraints as field 1
|
||||
|
||||
if not self.__fmt24hr:
|
||||
fields.append(ampmfield)
|
||||
|
||||
# set fields argument:
|
||||
maskededit_kwargs['fields'] = fields
|
||||
# allow for explicit size specification:
|
||||
if size != wx.DefaultSize:
|
||||
# override (and remove) "autofit" autoformat code in standard time formats:
|
||||
maskededit_kwargs['formatcodes'] = 'T!'
|
||||
|
||||
# This allows range validation if set
|
||||
maskededit_kwargs['validFunc'] = self.IsInBounds
|
||||
@ -379,16 +400,8 @@ class TimeCtrl(MaskedTextCtrl):
|
||||
# dynamically without affecting individual field constraint validation
|
||||
maskededit_kwargs['retainFieldValidation'] = True
|
||||
|
||||
# allow control over font selection:
|
||||
maskededit_kwargs['useFixedWidthFont'] = self.__useFixedWidthFont
|
||||
|
||||
# allow for explicit size specification:
|
||||
if size != wx.DefaultSize:
|
||||
# override (and remove) "autofit" autoformat code in standard time formats:
|
||||
maskededit_kwargs['formatcodes'] = 'T!'
|
||||
|
||||
# Now we can initialize the base control:
|
||||
MaskedTextCtrl.__init__(
|
||||
BaseMaskedTextCtrl.__init__(
|
||||
self, parent, id=id,
|
||||
pos=pos, size=size,
|
||||
style = style,
|
||||
@ -425,7 +438,7 @@ class TimeCtrl(MaskedTextCtrl):
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self._OnDoubleClick ) ## select field under cursor on dclick
|
||||
self.Bind(wx.EVT_KEY_DOWN, self._OnKeyDown ) ## capture control events not normally seen, eg ctrl-tab.
|
||||
self.Bind(wx.EVT_CHAR, self.__OnChar ) ## remove "shift" attribute from colon key event,
|
||||
## then call MaskedTextCtrl._OnChar with
|
||||
## then call BaseMaskedTextCtrl._OnChar with
|
||||
## the possibly modified event.
|
||||
self.Bind(wx.EVT_TEXT, self.__OnTextChange, self ) ## color control appropriately and EVT_TIMEUPDATE events
|
||||
|
||||
@ -442,6 +455,110 @@ class TimeCtrl(MaskedTextCtrl):
|
||||
self.BindSpinButton(spinButton) # bind spin button up/down events to this control
|
||||
|
||||
|
||||
def SetParameters(self, **kwargs):
|
||||
dbg('TimeCtrl::SetParameters(%s)' % repr(kwargs), indent=1)
|
||||
maskededit_kwargs = {}
|
||||
reset_format = False
|
||||
|
||||
if kwargs.has_key('display_seconds'):
|
||||
kwargs['displaySeconds'] = kwargs['display_seconds']
|
||||
del kwargs['display_seconds']
|
||||
if kwargs.has_key('format') and kwargs.has_key('displaySeconds'):
|
||||
del kwargs['displaySeconds'] # always apply format if specified
|
||||
|
||||
# assign keyword args as appropriate:
|
||||
for key, param_value in kwargs.items():
|
||||
if key not in TimeCtrl.valid_ctrl_params.keys():
|
||||
raise AttributeError('invalid keyword argument "%s"' % key)
|
||||
|
||||
if key == 'format':
|
||||
# handle both local or generic 'maskededit' autoformat codes:
|
||||
if param_value == 'HHMMSS' or param_value == 'TIMEHHMMSS':
|
||||
self.__displaySeconds = True
|
||||
self.__fmt24hr = False
|
||||
elif param_value == 'HHMM' or param_value == 'TIMEHHMM':
|
||||
self.__displaySeconds = False
|
||||
self.__fmt24hr = False
|
||||
elif param_value == '24HHMMSS' or param_value == '24HRTIMEHHMMSS':
|
||||
self.__displaySeconds = True
|
||||
self.__fmt24hr = True
|
||||
elif param_value == '24HHMM' or param_value == '24HRTIMEHHMM':
|
||||
self.__displaySeconds = False
|
||||
self.__fmt24hr = True
|
||||
else:
|
||||
raise AttributeError('"%s" is not a valid format' % param_value)
|
||||
reset_format = True
|
||||
|
||||
elif key in ("displaySeconds", "display_seconds") and not kwargs.has_key('format'):
|
||||
self.__displaySeconds = param_value
|
||||
reset_format = True
|
||||
|
||||
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 reset_format:
|
||||
if self.__fmt24hr:
|
||||
if self.__displaySeconds: maskededit_kwargs['autoformat'] = '24HRTIMEHHMMSS'
|
||||
else: maskededit_kwargs['autoformat'] = '24HRTIMEHHMM'
|
||||
|
||||
# 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<SV', validRegex='0\d|1\d|2[0123]', validRequired=True)
|
||||
else:
|
||||
if self.__displaySeconds: maskededit_kwargs['autoformat'] = 'TIMEHHMMSS'
|
||||
else: maskededit_kwargs['autoformat'] = 'TIMEHHMM'
|
||||
|
||||
# Set hour field to allow spaces (at start), right-insert,
|
||||
# require explicit field change, select entire field on entry,
|
||||
# and require a resultant valid entry to allow character entry:
|
||||
hourfield = Field(formatcodes='_0<rSV', validRegex='0[1-9]| [1-9]|1[012]', validRequired=True)
|
||||
ampmfield = Field(formatcodes='S', emptyInvalid = True, validRequired = True)
|
||||
|
||||
# Field 1 is always a zero-padded right-insert minute field,
|
||||
# similarly configured as above:
|
||||
minutefield = Field(formatcodes='0r<SV', validRegex='[0-5]\d', validRequired=True)
|
||||
|
||||
fields = [ hourfield, minutefield ]
|
||||
if self.__displaySeconds:
|
||||
fields.append(copy.copy(minutefield)) # second field has same constraints as field 1
|
||||
|
||||
if not self.__fmt24hr:
|
||||
fields.append(ampmfield)
|
||||
|
||||
# set fields argument:
|
||||
maskededit_kwargs['fields'] = fields
|
||||
|
||||
# This allows range validation if set
|
||||
maskededit_kwargs['validFunc'] = self.IsInBounds
|
||||
|
||||
# This allows range limits to affect insertion into control or not
|
||||
# dynamically without affecting individual field constraint validation
|
||||
maskededit_kwargs['retainFieldValidation'] = True
|
||||
|
||||
if hasattr(self, 'controlInitialized') and self.controlInitialized:
|
||||
self.SetCtrlParameters(**maskededit_kwargs) # set appropriate parameters
|
||||
|
||||
# Validate initial value and set if appropriate
|
||||
try:
|
||||
self.SetBounds(min, max)
|
||||
self.SetLimited(limited)
|
||||
self.SetValue(value)
|
||||
except:
|
||||
self.SetValue('12:00:00 AM')
|
||||
dbg(indent=0)
|
||||
return {} # no arguments to return
|
||||
else:
|
||||
dbg(indent=0)
|
||||
return maskededit_kwargs
|
||||
|
||||
|
||||
def BindSpinButton(self, sb):
|
||||
"""
|
||||
@ -496,7 +613,7 @@ class TimeCtrl(MaskedTextCtrl):
|
||||
elif as_mxDateTimeDelta:
|
||||
value = DateTime.DateTimeDelta(0, value.GetHour(), value.GetMinute(), value.GetSecond())
|
||||
else:
|
||||
value = MaskedTextCtrl.GetValue(self)
|
||||
value = BaseMaskedTextCtrl.GetValue(self)
|
||||
return value
|
||||
|
||||
|
||||
@ -888,6 +1005,16 @@ class TimeCtrl(MaskedTextCtrl):
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def SetFormat(self, format):
|
||||
self.SetParameters(format=format)
|
||||
|
||||
def GetFormat(self):
|
||||
if self.__displaySeconds:
|
||||
if self.__fmt24hr: return '24HHMMSS'
|
||||
else: return 'HHMMSS'
|
||||
else:
|
||||
if self.__fmt24hr: return '24HHMM'
|
||||
else: return 'HHMM'
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------
|
||||
# these are private functions and overrides:
|
||||
@ -896,7 +1023,7 @@ class TimeCtrl(MaskedTextCtrl):
|
||||
def __OnTextChange(self, event=None):
|
||||
dbg('TimeCtrl::OnTextChange', indent=1)
|
||||
|
||||
# Allow wxMaskedtext base control to color as appropriate,
|
||||
# Allow Maskedtext base control to color as appropriate,
|
||||
# and Skip the EVT_TEXT event (if appropriate.)
|
||||
##! WS: For some inexplicable reason, every wxTextCtrl.SetValue()
|
||||
## call is generating two (2) EVT_TEXT events. (!)
|
||||
@ -905,7 +1032,7 @@ class TimeCtrl(MaskedTextCtrl):
|
||||
## event iff the value has actually changed. The masked edit
|
||||
## OnTextChange routine does this, and returns True on a valid event,
|
||||
## False otherwise.
|
||||
if not MaskedTextCtrl._OnTextChange(self, event):
|
||||
if not BaseMaskedTextCtrl._OnTextChange(self, event):
|
||||
return
|
||||
|
||||
dbg('firing TimeUpdatedEvent...')
|
||||
@ -922,7 +1049,7 @@ class TimeCtrl(MaskedTextCtrl):
|
||||
point is lost when the focus shifts to the spin button.
|
||||
"""
|
||||
dbg('TimeCtrl::SetInsertionPoint', pos, indent=1)
|
||||
MaskedTextCtrl.SetInsertionPoint(self, pos) # (causes EVT_TEXT event to fire)
|
||||
BaseMaskedTextCtrl.SetInsertionPoint(self, pos) # (causes EVT_TEXT event to fire)
|
||||
self.__posCurrent = self.GetInsertionPoint()
|
||||
dbg(indent=0)
|
||||
|
||||
@ -941,7 +1068,7 @@ class TimeCtrl(MaskedTextCtrl):
|
||||
sel_to = cell_end
|
||||
|
||||
self.__bSelection = sel_start != sel_to
|
||||
MaskedTextCtrl.SetSelection(self, sel_start, sel_to)
|
||||
BaseMaskedTextCtrl.SetSelection(self, sel_start, sel_to)
|
||||
dbg(indent=0)
|
||||
|
||||
|
||||
@ -998,7 +1125,7 @@ class TimeCtrl(MaskedTextCtrl):
|
||||
if keycode == ord(':'):
|
||||
dbg('colon seen! removing shift attribute')
|
||||
event.m_shiftDown = False
|
||||
MaskedTextCtrl._OnChar(self, event ) ## handle each keypress
|
||||
BaseMaskedTextCtrl._OnChar(self, event ) ## handle each keypress
|
||||
dbg(indent=0)
|
||||
|
||||
|
||||
@ -1084,10 +1211,10 @@ class TimeCtrl(MaskedTextCtrl):
|
||||
converts it to a string appropriate for the format of the control.
|
||||
"""
|
||||
if self.__fmt24hr:
|
||||
if self.__display_seconds: strval = wxdt.Format('%H:%M:%S')
|
||||
if self.__displaySeconds: strval = wxdt.Format('%H:%M:%S')
|
||||
else: strval = wxdt.Format('%H:%M')
|
||||
else:
|
||||
if self.__display_seconds: strval = wxdt.Format('%I:%M:%S %p')
|
||||
if self.__displaySeconds: strval = wxdt.Format('%I:%M:%S %p')
|
||||
else: strval = wxdt.Format('%I:%M %p')
|
||||
|
||||
return strval
|
||||
@ -1178,3 +1305,11 @@ if __name__ == '__main__':
|
||||
app.MainLoop()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
i=0
|
||||
## Version 1.2
|
||||
## 1. Changed parameter name display_seconds to displaySeconds, to follow
|
||||
## other masked edit conventions.
|
||||
## 2. Added format parameter, to remove need to use both fmt24hr and displaySeconds.
|
||||
## 3. Changed inheritance to use BaseMaskedTextCtrl, to remove exposure of
|
||||
## nonsensical parameter methods from the control, so it will work
|
||||
## properly with Boa.
|
||||
|
Loading…
Reference in New Issue
Block a user