Added new MaskedEditControl code from Will Sadkin. The modules are

now locaed in their own sub-package, wx.lib.masked.  Demos updated.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@26874 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn 2004-04-19 23:24:37 +00:00
parent 6cffbf02c0
commit c878ceeae8
17 changed files with 1592 additions and 1420 deletions

View File

@ -4,9 +4,8 @@ import sys
import traceback
import wx
import wx.lib.maskededit as med
import wx.lib.maskedctrl as mctl
import wx.lib.scrolledpanel as scroll
import wx.lib.masked as masked
import wx.lib.scrolledpanel as scroll
class demoMixin:
@ -18,7 +17,7 @@ class demoMixin:
mask = wx.StaticText( self, -1, "Mask Value" )
formatcode = wx.StaticText( self, -1, "Format" )
regex = wx.StaticText( self, -1, "Regexp Validator(opt.)" )
ctrl = wx.StaticText( self, -1, "MaskedTextCtrl" )
ctrl = wx.StaticText( self, -1, "Masked TextCtrl" )
description.SetFont( wx.Font(9, wx.SWISS, wx.NORMAL, wx.BOLD))
mask.SetFont( wx.Font(9, wx.SWISS, wx.NORMAL, wx.BOLD))
@ -41,7 +40,7 @@ class demoMixin:
sizer.Add( wx.StaticText( self, -1, control[4]) )
if control in controls:
newControl = med.MaskedTextCtrl( self, -1, "",
newControl = masked.TextCtrl( self, -1, "",
mask = control[1],
excludeChars = control[2],
formatcodes = control[3],
@ -79,7 +78,7 @@ class demoPage1(scroll.ScrolledPanel, demoMixin):
self.editList = []
label = wx.StaticText( self, -1, """\
Here are some basic MaskedTextCtrls to give you an idea of what you can do
Here are some basic masked TextCtrls to give you an idea of what you can do
with this control. Note that all controls have been auto-sized by including 'F' in
the format codes.
@ -152,8 +151,8 @@ class demoPage2(scroll.ScrolledPanel, demoMixin):
label = wx.StaticText( self, -1, """\
All these controls have been created by passing a single parameter, the autoformat code,
and use the factory class MaskedCtrl with its default controlType.
The maskededit module contains an internal dictionary of types and formats (autoformats).
and use the factory class masked.Ctrl with its default controlType.
The masked package contains an internal dictionary of types and formats (autoformats).
Many of these already do complicated validation; To see some examples, try
29 Feb 2002 vs. 2004 for the date formats, or email address validation.
""")
@ -163,7 +162,7 @@ Many of these already do complicated validation; To see some examples, try
description = wx.StaticText( self, -1, "Description")
autofmt = wx.StaticText( self, -1, "AutoFormat Code")
ctrl = wx.StaticText( self, -1, "MaskedCtrl")
ctrl = wx.StaticText( self, -1, "Masked Ctrl")
description.SetFont( wx.Font( 9, wx.SWISS, wx.NORMAL, wx.BOLD ) )
autofmt.SetFont( wx.Font( 9, wx.SWISS, wx.NORMAL, wx.BOLD ) )
@ -174,10 +173,10 @@ Many of these already do complicated validation; To see some examples, try
grid.Add( autofmt, 0, wx.ALIGN_LEFT )
grid.Add( ctrl, 0, wx.ALIGN_LEFT )
for autoformat, desc in med.autoformats:
for autoformat, desc in masked.autoformats:
grid.Add( wx.StaticText( self, -1, desc), 0, wx.ALIGN_LEFT )
grid.Add( wx.StaticText( self, -1, autoformat), 0, wx.ALIGN_LEFT )
grid.Add( mctl.MaskedCtrl( self, -1, "",
grid.Add( masked.Ctrl( self, -1, "",
autoformat = autoformat,
demo = True,
name = autoformat),
@ -197,7 +196,7 @@ class demoPage3(scroll.ScrolledPanel, demoMixin):
self.editList = []
label = wx.StaticText( self, -1, """\
Here MaskedTextCtrls that have default values. The states
Here masked TextCtrls that have default values. The states
control has a list of valid values, and the unsigned integer
has a legal range specified.
""")
@ -215,7 +214,7 @@ has a legal range specified.
controls = [
#description mask excl format regexp range,list,initial
("U.S. State (2 char)", "AA", "", 'F!_', "[A-Z]{2}", '',med.states, med.states[0]),
("U.S. State (2 char)", "AA", "", 'F!_', "[A-Z]{2}", '', masked.states, masked.states[0]),
("Integer (signed)", "#{6}", "", 'F-_', "", '','', ' 0 '),
("Integer (unsigned)\n(1-399)","######", "", 'F_', "", (1,399),'', '1 '),
("Float (signed)", "#{6}.#{9}", "", 'F-_R', "", '','', '000000.000000000'),
@ -256,7 +255,7 @@ Page Up and Shift-Up arrow will similarly cycle backwards through the list.
description = wx.StaticText( self, -1, "Description" )
autofmt = wx.StaticText( self, -1, "AutoFormat Code" )
fields = wx.StaticText( self, -1, "Field Objects" )
ctrl = wx.StaticText( self, -1, "MaskedTextCtrl" )
ctrl = wx.StaticText( self, -1, "Masked TextCtrl" )
description.SetFont( wx.Font( 9, wx.SWISS, wx.NORMAL, wx.BOLD ) )
autofmt.SetFont( wx.Font( 9, wx.SWISS, wx.NORMAL, wx.BOLD ) )
@ -270,7 +269,7 @@ Page Up and Shift-Up arrow will similarly cycle backwards through the list.
grid.Add( ctrl, 0, wx.ALIGN_LEFT )
autoformat = "USPHONEFULLEXT"
fieldsDict = {0: med.Field(choices=["617","781","508","978","413"], choiceRequired=True)}
fieldsDict = {0: masked.Field(choices=["617","781","508","978","413"], choiceRequired=True)}
fieldsLabel = """\
{0: Field(choices=[
"617","781",
@ -279,7 +278,7 @@ Page Up and Shift-Up arrow will similarly cycle backwards through the list.
grid.Add( wx.StaticText( self, -1, "Restricted Area Code"), 0, wx.ALIGN_LEFT )
grid.Add( wx.StaticText( self, -1, autoformat), 0, wx.ALIGN_LEFT )
grid.Add( wx.StaticText( self, -1, fieldsLabel), 0, wx.ALIGN_LEFT )
grid.Add( med.MaskedTextCtrl( self, -1, "",
grid.Add( masked.TextCtrl( self, -1, "",
autoformat = autoformat,
fields = fieldsDict,
demo = True,
@ -287,12 +286,12 @@ Page Up and Shift-Up arrow will similarly cycle backwards through the list.
0, wx.ALIGN_LEFT )
autoformat = "EXPDATEMMYY"
fieldsDict = {1: med.Field(choices=["03", "04", "05"], choiceRequired=True)}
fieldsDict = {1: masked.Field(choices=["03", "04", "05"], choiceRequired=True)}
fieldsLabel = """\
{1: Field(choices=[
"03", "04", "05"],
choiceRequired=True)}"""
exp = med.MaskedTextCtrl( self, -1, "",
exp = masked.TextCtrl( self, -1, "",
autoformat = autoformat,
fields = fieldsDict,
demo = True,
@ -303,15 +302,15 @@ Page Up and Shift-Up arrow will similarly cycle backwards through the list.
grid.Add( wx.StaticText( self, -1, fieldsLabel), 0, wx.ALIGN_LEFT )
grid.Add( exp, 0, wx.ALIGN_LEFT )
fieldsDict = {0: med.Field(choices=["02134","02155"], choiceRequired=True),
1: med.Field(choices=["1234", "5678"], choiceRequired=False)}
fieldsDict = {0: masked.Field(choices=["02134","02155"], choiceRequired=True),
1: masked.Field(choices=["1234", "5678"], choiceRequired=False)}
fieldsLabel = """\
{0: Field(choices=["02134","02155"],
choiceRequired=True),
1: Field(choices=["1234", "5678"],
choiceRequired=False)}"""
autoformat = "USZIPPLUS4"
zip = med.MaskedTextCtrl( self, -1, "",
zip = masked.TextCtrl( self, -1, "",
autoformat = autoformat,
fields = fieldsDict,
demo = True,
@ -336,7 +335,7 @@ class demoPage5(scroll.ScrolledPanel, demoMixin):
labelMaskedCombos = wx.StaticText( self, -1, """\
These are some examples of MaskedComboBox:""")
These are some examples of masked.ComboBox:""")
labelMaskedCombos.SetForegroundColour( "Blue" )
@ -344,8 +343,8 @@ These are some examples of MaskedComboBox:""")
A state selector; only
"legal" values can be
entered:""")
statecode = med.MaskedComboBox( self, -1, med.states[0],
choices = med.states,
statecode = masked.ComboBox( self, -1, masked.states[0],
choices = masked.states,
autoformat="USSTATE")
label_statename = wx.StaticText( self, -1, """\
@ -353,9 +352,9 @@ A state name selector,
with auto-select:""")
# Create this one using factory function:
statename = mctl.MaskedCtrl( self, -1, med.state_names[0],
controlType = mctl.controlTypes.MASKEDCOMBO,
choices = med.state_names,
statename = masked.Ctrl( self, -1, masked.state_names[0],
controlType = masked.controlTypes.COMBO,
choices = masked.state_names,
autoformat="USSTATENAME",
autoSelect=True)
statename.SetCtrlParameters(formatcodes = 'F!V_')
@ -363,8 +362,8 @@ with auto-select:""")
numerators = [ str(i) for i in range(1, 4) ]
denominators = [ string.ljust(str(i), 2) for i in [2,3,4,5,8,16,32,64] ]
fieldsDict = {0: med.Field(choices=numerators, choiceRequired=False),
1: med.Field(choices=denominators, choiceRequired=True)}
fieldsDict = {0: masked.Field(choices=numerators, choiceRequired=False),
1: masked.Field(choices=denominators, choiceRequired=True)}
choices = []
for n in numerators:
for d in denominators:
@ -377,8 +376,8 @@ A masked ComboBox for fraction selection.
Choices for each side of the fraction can
be selected with PageUp/Down:""")
fraction = mctl.MaskedCtrl( self, -1, "",
controlType = mctl.MASKEDCOMBO,
fraction = masked.Ctrl( self, -1, "",
controlType = masked.controlTypes.COMBO,
choices = choices,
choiceRequired = True,
mask = "#/##",
@ -392,7 +391,7 @@ A masked ComboBox to validate
text from a list of numeric codes:""")
choices = ["91", "136", "305", "4579"]
code = med.MaskedComboBox( self, -1, choices[0],
code = masked.ComboBox( self, -1, choices[0],
choices = choices,
choiceRequired = True,
formatcodes = "F_r",
@ -402,8 +401,8 @@ text from a list of numeric codes:""")
Programmatically set
choice sets:""")
self.list_selector = wx.ComboBox(self, -1, '', choices = ['list1', 'list2', 'list3'])
self.dynamicbox = mctl.MaskedCtrl( self, -1, ' ',
controlType = mctl.controlTypes.MASKEDCOMBO,
self.dynamicbox = masked.Ctrl( self, -1, ' ',
controlType = masked.controlTypes.COMBO,
mask = 'XXXX',
formatcodes = 'F_',
# these are to give dropdown some initial height,
@ -415,23 +414,23 @@ choice sets:""")
labelIpAddrs = wx.StaticText( self, -1, """\
Here are some examples of IpAddrCtrl, a control derived from MaskedTextCtrl:""")
Here are some examples of IpAddrCtrl, a control derived from masked.TextCtrl:""")
labelIpAddrs.SetForegroundColour( "Blue" )
label_ipaddr1 = wx.StaticText( self, -1, "An empty control:")
ipaddr1 = med.IpAddrCtrl( self, -1, style = wx.TE_PROCESS_TAB )
ipaddr1 = masked.IpAddrCtrl( self, -1, style = wx.TE_PROCESS_TAB )
label_ipaddr2 = wx.StaticText( self, -1, "A restricted mask:")
ipaddr2 = med.IpAddrCtrl( self, -1, mask=" 10. 1.109.###" )
ipaddr2 = masked.IpAddrCtrl( self, -1, mask=" 10. 1.109.###" )
label_ipaddr3 = wx.StaticText( self, -1, """\
A control with restricted legal values:
10. (1|2) . (129..255) . (0..255)""")
ipaddr3 = mctl.MaskedCtrl( self, -1,
controlType = mctl.controlTypes.IPADDR,
ipaddr3 = masked.Ctrl( self, -1,
controlType = masked.controlTypes.IPADDR,
mask=" 10. #.###.###")
ipaddr3.SetFieldParameters(0, validRegex="1|2",validRequired=False ) # requires entry to match or not allowed
@ -441,22 +440,22 @@ A control with restricted legal values:
labelNumerics = wx.StaticText( self, -1, """\
Here are some useful configurations of a MaskedTextCtrl for integer and floating point input that still treat
the control as a text control. (For a true numeric control, check out the MaskedNumCtrl class!)""")
Here are some useful configurations of a masked.TextCtrl for integer and floating point input that still treat
the control as a text control. (For a true numeric control, check out the masked.NumCtrl class!)""")
labelNumerics.SetForegroundColour( "Blue" )
label_intctrl1 = wx.StaticText( self, -1, """\
An integer entry control with
shifting insert enabled:""")
self.intctrl1 = med.MaskedTextCtrl(self, -1, name='intctrl', mask="#{9}", formatcodes = '_-,F>')
self.intctrl1 = masked.TextCtrl(self, -1, name='intctrl', mask="#{9}", formatcodes = '_-,F>')
label_intctrl2 = wx.StaticText( self, -1, """\
Right-insert integer entry:""")
self.intctrl2 = med.MaskedTextCtrl(self, -1, name='intctrl', mask="#{9}", formatcodes = '_-,Fr')
self.intctrl2 = masked.TextCtrl(self, -1, name='intctrl', mask="#{9}", formatcodes = '_-,Fr')
label_floatctrl = wx.StaticText( self, -1, """\
A floating point entry control
with right-insert for ordinal:""")
self.floatctrl = med.MaskedTextCtrl(self, -1, name='floatctrl', mask="#{9}.#{2}", formatcodes="F,_-R", useParensForNegatives=False)
self.floatctrl = masked.TextCtrl(self, -1, name='floatctrl', mask="#{9}.#{2}", formatcodes="F,_-R", useParensForNegatives=False)
self.floatctrl.SetFieldParameters(0, formatcodes='r<', validRequired=True) # right-insert, require explicit cursor movement to change fields
self.floatctrl.SetFieldParameters(1, defaultValue='00') # don't allow blank fraction
@ -588,7 +587,7 @@ with right-insert for ordinal:""")
formatcodes += 'r'
mask = '###'
else:
choices = med.states
choices = masked.states
mask = 'AA'
formatcodes += '!'
self.dynamicbox.SetCtrlParameters( mask = mask,
@ -628,15 +627,15 @@ def runTest(frame, nb, log):
def RunStandalone():
app = wx.PySimpleApp()
frame = wx.Frame(None, -1, "Test MaskedTextCtrl", size=(640, 480))
frame = wx.Frame(None, -1, "Test MaskedEditCtrls", size=(640, 480))
win = TestMaskedTextCtrls(frame, -1, sys.stdout)
frame.Show(True)
app.MainLoop()
#----------------------------------------------------------------------------
import wx.lib.masked.maskededit as maskededit
overview = """<html>
<PRE><FONT SIZE=-1>
""" + med.__doc__ + """
""" + maskededit.__doc__ + """
</FONT></PRE>
"""

View File

@ -4,8 +4,8 @@ import sys
import traceback
import wx
import wx.lib.maskededit as me
import wx.lib.maskednumctrl as mnum
from wx.lib import masked
#----------------------------------------------------------------------
class TestPanel( wx.Panel ):
@ -16,40 +16,40 @@ class TestPanel( wx.Panel ):
panel = wx.Panel( self, -1 )
header = wx.StaticText(panel, -1, """\
This shows the various options for MaskedNumCtrl.
This shows the various options for masked.NumCtrl.
The controls at the top reconfigure the resulting control at the bottom.
""")
header.SetForegroundColour( "Blue" )
intlabel = wx.StaticText( panel, -1, "Integer width:" )
self.integerwidth = mnum.MaskedNumCtrl(
self.integerwidth = masked.NumCtrl(
panel, value=10, integerWidth=2, allowNegative=False
)
fraclabel = wx.StaticText( panel, -1, "Fraction width:" )
self.fractionwidth = mnum.MaskedNumCtrl(
panel, value=0, integerWidth=2, allowNegative=False
self.fractionwidth = masked.NumCtrl(
panel, value=0, integerWidth=2, allowNegative=False
)
groupcharlabel = wx.StaticText( panel,-1, "Grouping char:" )
self.groupchar = me.MaskedTextCtrl(
self.groupchar = masked.TextCtrl(
panel, -1, value=',', mask='&', excludeChars = '-()',
formatcodes='F', emptyInvalid=True, validRequired=True
)
decimalcharlabel = wx.StaticText( panel,-1, "Decimal char:" )
self.decimalchar = me.MaskedTextCtrl(
self.decimalchar = masked.TextCtrl(
panel, -1, value='.', mask='&', excludeChars = '-()',
formatcodes='F', emptyInvalid=True, validRequired=True
)
self.set_min = wx.CheckBox( panel, -1, "Set minimum value:" )
# Create this MaskedNumCtrl using factory, to show how:
self.min = mnum.MaskedNumCtrl( panel, integerWidth=5, fractionWidth=2 )
# Create this masked.NumCtrl using factory, to show how:
self.min = masked.Ctrl( panel, integerWidth=5, fractionWidth=2, controlType=masked.controlTypes.NUMBER )
self.min.Enable( False )
self.set_max = wx.CheckBox( panel, -1, "Set maximum value:" )
self.max = mnum.MaskedNumCtrl( panel, integerWidth=5, fractionWidth=2 )
self.max = masked.NumCtrl( panel, integerWidth=5, fractionWidth=2 )
self.max.Enable( False )
@ -68,7 +68,7 @@ The controls at the top reconfigure the resulting control at the bottom.
font.SetWeight(wx.BOLD)
label.SetFont(font)
self.target_ctl = mnum.MaskedNumCtrl( panel, -1, name="target control" )
self.target_ctl = masked.NumCtrl( panel, -1, name="target control" )
label_numselect = wx.StaticText( panel, -1, """\
Programmatically set the above
@ -141,15 +141,15 @@ value entry ctrl:""")
panel.Move( (50,10) )
self.panel = panel
self.Bind(mnum.EVT_MASKEDNUM, self.OnSetIntWidth, self.integerwidth )
self.Bind(mnum.EVT_MASKEDNUM, self.OnSetFractionWidth, self.fractionwidth )
self.Bind(masked.EVT_NUM, self.OnSetIntWidth, self.integerwidth )
self.Bind(masked.EVT_NUM, self.OnSetFractionWidth, self.fractionwidth )
self.Bind(wx.EVT_TEXT, self.OnSetGroupChar, self.groupchar )
self.Bind(wx.EVT_TEXT, self.OnSetDecimalChar, self.decimalchar )
self.Bind(wx.EVT_CHECKBOX, self.OnSetMin, self.set_min )
self.Bind(wx.EVT_CHECKBOX, self.OnSetMax, self.set_max )
self.Bind(mnum.EVT_MASKEDNUM, self.SetTargetMinMax, self.min )
self.Bind(mnum.EVT_MASKEDNUM, self.SetTargetMinMax, self.max )
self.Bind(masked.EVT_NUM, self.SetTargetMinMax, self.min )
self.Bind(masked.EVT_NUM, self.SetTargetMinMax, self.max )
self.Bind(wx.EVT_CHECKBOX, self.SetTargetMinMax, self.limit_target )
self.Bind(wx.EVT_CHECKBOX, self.OnSetAllowNone, self.allow_none )
@ -158,7 +158,7 @@ value entry ctrl:""")
self.Bind(wx.EVT_CHECKBOX, self.OnSetUseParens, self.use_parens )
self.Bind(wx.EVT_CHECKBOX, self.OnSetSelectOnEntry, self.select_on_entry )
self.Bind(mnum.EVT_MASKEDNUM, self.OnTargetChange, self.target_ctl )
self.Bind(masked.EVT_NUM, self.OnTargetChange, self.target_ctl )
self.Bind(wx.EVT_COMBOBOX, self.OnNumberSelect, self.numselect )
@ -323,6 +323,7 @@ def runTest( frame, nb, log ):
return win
#----------------------------------------------------------------------
import wx.lib.masked.numctrl as mnum
overview = mnum.__doc__
if __name__ == '__main__':

View File

@ -1,12 +1,12 @@
#
#
# 11/21/2003 - Jeff Grimmett (grimmtooth@softhome.net)
#
# o presense of spin control causing probs (see spin ctrl demo for details)
#
#
import wx
import wx.lib.timectrl as timectl
import wx.lib.scrolledpanel as scrolled
import wx
import wx.lib.scrolledpanel as scrolled
import wx.lib.masked as masked
#----------------------------------------------------------------------
@ -18,21 +18,21 @@ class TestPanel( scrolled.ScrolledPanel ):
text1 = wx.StaticText( self, -1, "12-hour format:")
self.time12 = timectl.TimeCtrl( self, -1, name="12 hour control" )
self.time12 = masked.TimeCtrl( self, -1, name="12 hour control" )
spin1 = wx.SpinButton( self, -1, wx.DefaultPosition, (-1,20), 0 )
self.time12.BindSpinButton( spin1 )
text2 = wx.StaticText( self, -1, "24-hour format:")
spin2 = wx.SpinButton( self, -1, wx.DefaultPosition, (-1,20), 0 )
self.time24 = timectl.TimeCtrl(
self, -1, name="24 hour control", fmt24hr=True,
spinButton = spin2
self.time24 = masked.TimeCtrl(
self, -1, name="24 hour control", fmt24hr=True,
spinButton = spin2
)
text3 = wx.StaticText( self, -1, "No seconds\nor spin button:")
self.spinless_ctrl = timectl.TimeCtrl(
self, -1, name="spinless control",
display_seconds = False
self.spinless_ctrl = masked.TimeCtrl(
self, -1, name="spinless control",
display_seconds = False
)
grid = wx.FlexGridSizer( 0, 2, 10, 5 )
@ -54,8 +54,8 @@ class TestPanel( scrolled.ScrolledPanel ):
buttonChange = wx.Button( self, -1, "Change Controls")
self.radio12to24 = wx.RadioButton(
self, -1, "Copy 12-hour time to 24-hour control",
wx.DefaultPosition, wx.DefaultSize, wx.RB_GROUP
self, -1, "Copy 12-hour time to 24-hour control",
wx.DefaultPosition, wx.DefaultSize, wx.RB_GROUP
)
self.radio24to12 = wx.RadioButton(
@ -86,17 +86,17 @@ class TestPanel( scrolled.ScrolledPanel ):
self.set_bounds = wx.CheckBox( self, -1, "Set time bounds:" )
minlabel = wx.StaticText( self, -1, "minimum time:" )
self.min = timectl.TimeCtrl( self, -1, name="min", display_seconds = False )
self.min = masked.TimeCtrl( self, -1, name="min", display_seconds = False )
self.min.Enable( False )
maxlabel = wx.StaticText( self, -1, "maximum time:" )
self.max = timectl.TimeCtrl( self, -1, name="max", display_seconds = False )
self.max = masked.TimeCtrl( self, -1, name="max", display_seconds = False )
self.max.Enable( False )
self.limit_check = wx.CheckBox( self, -1, "Limit control" )
label = wx.StaticText( self, -1, "Resulting time control:" )
self.target_ctrl = timectl.TimeCtrl( self, -1, name="new" )
self.target_ctrl = masked.TimeCtrl( self, -1, name="new" )
grid2 = wx.FlexGridSizer( 0, 2, 0, 0 )
grid2.Add( (20, 0), 0, wx.ALIGN_LEFT|wx.ALL, 5 )
@ -142,14 +142,14 @@ class TestPanel( scrolled.ScrolledPanel ):
self.SetupScrolling()
self.Bind(wx.EVT_BUTTON, self.OnButtonClick, buttonChange )
self.Bind(timectl.EVT_TIMEUPDATE, self.OnTimeChange, self.time12 )
self.Bind(timectl.EVT_TIMEUPDATE, self.OnTimeChange, self.time24 )
self.Bind(timectl.EVT_TIMEUPDATE, self.OnTimeChange, self.spinless_ctrl )
self.Bind(masked.EVT_TIMEUPDATE, self.OnTimeChange, self.time12 )
self.Bind(masked.EVT_TIMEUPDATE, self.OnTimeChange, self.time24 )
self.Bind(masked.EVT_TIMEUPDATE, self.OnTimeChange, self.spinless_ctrl )
self.Bind(wx.EVT_CHECKBOX, self.OnBoundsCheck, self.set_bounds )
self.Bind(wx.EVT_CHECKBOX, self.SetTargetMinMax, self.limit_check )
self.Bind(timectl.EVT_TIMEUPDATE, self.SetTargetMinMax, self.min )
self.Bind(timectl.EVT_TIMEUPDATE, self.SetTargetMinMax, self.max )
self.Bind(timectl.EVT_TIMEUPDATE, self.OnTimeChange, self.target_ctrl )
self.Bind(masked.EVT_TIMEUPDATE, self.SetTargetMinMax, self.min )
self.Bind(masked.EVT_TIMEUPDATE, self.SetTargetMinMax, self.max )
self.Bind(masked.EVT_TIMEUPDATE, self.OnTimeChange, self.target_ctrl )
def OnTimeChange( self, event ):
@ -204,7 +204,7 @@ class TestPanel( scrolled.ScrolledPanel ):
min, max = None, None
cur_min, cur_max = self.target_ctrl.GetBounds()
print cur_min, min
if min and (min != cur_min): self.target_ctrl.SetMin( min )
if max and (max != cur_max): self.target_ctrl.SetMax( max )
@ -225,11 +225,11 @@ def runTest( frame, nb, log ):
return win
#----------------------------------------------------------------------
import wx.lib.masked.timectrl as timectl
overview = timectl.__doc__
if __name__ == '__main__':
import sys,os
import run
run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
run.main(['', os.path.basename(sys.argv[0])])

View File

@ -17,6 +17,9 @@ Added some convenience methods to wx.Bitmap: SetSize, GetSize, and
wx.EmptyBitmap can be called with a wx.Size (or a 2-element sequence)
object too. Similar changes were done for wx.Image as well.
Added new MaskedEditControl code from Will Sadkin. The modules are
now locaed in their own sub-package, wx.lib.masked. Demos updated.

View File

@ -619,6 +619,14 @@ Similarly, the wxSystemSettings backwards compatibiility aliases for
GetSystemColour, GetSystemFont and GetSystemMetric have also gone into
the bit-bucket. Use GetColour, GetFont and GetMetric instead.
Use the Python True/False constants instead of the true, TRUE, false,
FALSE that used to be provided with wxPython.
Use None instead of the ancient and should have been removed a long
time ago wx.NULL alias.
wx.TreeCtrl no longer needs to be passed the cookie variable as the
2nd parameter. It still returns it though, for use with GetNextChild.
The wx.NO_FULL_REPAINT_ON_RESIZE style is now the default style for
all windows. The name still exists for compatibility, but it is set
@ -667,3 +675,8 @@ functions in wxPython for parameters that are expecting an integer.
If the object is not already an integer then it will be asked to
convert itself to one. A similar conversion fragment is in place for
parameters that expect floating point values.
**[Changed in 2.5.1.6]** The MaskedEditCtrl modules have been moved
to their own sub-package, wx.lib.masked. See the docstrings and demo
for changes in capabilities, usage, etc.

View File

@ -0,0 +1,20 @@
#----------------------------------------------------------------------
# Name: wxPython.lib.masked
# Purpose: A package containing the masked edit controls
#
# Author: Will Sadkin, Jeff Childers
#
# Created: 6-Mar-2004
# RCS-ID: $Id$
# Copyright: (c) 2004
# License: wxWidgets license
#----------------------------------------------------------------------
# import relevant external symbols into package namespace:
from maskededit import *
from textctrl import BaseMaskedTextCtrl, TextCtrl
from combobox import BaseMaskedComboBox, 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
from ctrl import Ctrl, controlTypes

View File

@ -0,0 +1,540 @@
#----------------------------------------------------------------------------
# Name: masked.combobox.py
# Authors: Will Sadkin
# Email: wsadkin@nameconnector.com
# Created: 02/11/2003
# Copyright: (c) 2003 by Will Sadkin, 2003
# RCS-ID: $Id$
# License: wxWidgets license
#----------------------------------------------------------------------------
#
# This masked edit class allows for the semantics of masked controls
# to be applied to combo boxes.
#
#----------------------------------------------------------------------------
import wx
from wx.lib.masked import *
# jmg 12/9/03 - when we cut ties with Py 2.2 and earlier, this would
# be a good place to implement the 2.3 logger class
from wx.tools.dbg import Logger
dbg = Logger()
##dbg(enable=0)
## ---------- ---------- ---------- ---------- ---------- ---------- ----------
## Because calling SetSelection programmatically does not fire EVT_COMBOBOX
## events, we have to do it ourselves when we auto-complete.
class MaskedComboBoxSelectEvent(wx.PyCommandEvent):
def __init__(self, id, selection = 0, object=None):
wx.PyCommandEvent.__init__(self, wx.wxEVT_COMMAND_COMBOBOX_SELECTED, id)
self.__selection = selection
self.SetEventObject(object)
def GetSelection(self):
"""Retrieve the value of the control at the time
this event was generated."""
return self.__selection
class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
"""
This masked edit control adds the ability to use a masked input
on a combobox, and do auto-complete of such values.
"""
def __init__( self, parent, id=-1, value = '',
pos = wx.DefaultPosition,
size = wx.DefaultSize,
choices = [],
style = wx.CB_DROPDOWN,
validator = wx.DefaultValidator,
name = "maskedComboBox",
setupEventHandling = True, ## setup event handling by default):
**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
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.__init__(self, parent, id, value='',
pos=pos, size = size,
choices=choices, style=style|wx.WANTS_CHARS,
validator=validator,
name=name)
self.controlInitialized = True
# Set control font - fixed width by default
self._setFont()
if self._autofit:
self.SetClientSize(self._CalcSize())
if value:
# ensure value is width of the mask of the control:
if self._ctrl_constraints._alignRight:
value = value.rjust(self._masklength)
else:
value = value.ljust(self._masklength)
if self.__readonly:
self.SetStringSelection(value)
else:
self._SetInitialValue(value)
self._SetKeycodeHandler(wx.WXK_UP, self.OnSelectChoice)
self._SetKeycodeHandler(wx.WXK_DOWN, self.OnSelectChoice)
if setupEventHandling:
## Setup event handlers
self.Bind(wx.EVT_SET_FOCUS, self._OnFocus ) ## defeat automatic full selection
self.Bind(wx.EVT_KILL_FOCUS, self._OnKillFocus ) ## run internal validator
self.Bind(wx.EVT_LEFT_DCLICK, self._OnDoubleClick) ## select field under cursor on dclick
self.Bind(wx.EVT_RIGHT_UP, self._OnContextMenu ) ## bring up an appropriate context menu
self.Bind(wx.EVT_CHAR, self._OnChar ) ## handle each keypress
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown ) ## for special processing of up/down keys
self.Bind(wx.EVT_KEY_DOWN, self._OnKeyDown ) ## for processing the rest of the control keys
## (next in evt chain)
self.Bind(wx.EVT_TEXT, self._OnTextChange ) ## color control appropriately & keep
## track of previous value for undo
def __repr__(self):
return "<MaskedComboBox: %s>" % self.GetValue()
def _CalcSize(self, size=None):
"""
Calculate automatic size if allowed; augment base mixin function
to account for the selector button.
"""
size = self._calcSize(size)
return (size[0]+20, size[1])
def _GetSelection(self):
"""
Allow mixin to get the text selection of this control.
REQUIRED by any class derived from MaskedEditMixin.
"""
return self.GetMark()
def _SetSelection(self, sel_start, sel_to):
"""
Allow mixin to set the text selection of this control.
REQUIRED by any class derived from MaskedEditMixin.
"""
return self.SetMark( sel_start, sel_to )
def _GetInsertionPoint(self):
return self.GetInsertionPoint()
def _SetInsertionPoint(self, pos):
self.SetInsertionPoint(pos)
def _GetValue(self):
"""
Allow mixin to get the raw value of the control with this function.
REQUIRED by any class derived from MaskedEditMixin.
"""
return self.GetValue()
def _SetValue(self, value):
"""
Allow mixin to set the raw value of the control with this function.
REQUIRED by any class derived from MaskedEditMixin.
"""
# For wxComboBox, ensure that values are properly padded so that
# if varying length choices are supplied, they always show up
# in the window properly, and will be the appropriate length
# to match the mask:
if self._ctrl_constraints._alignRight:
value = value.rjust(self._masklength)
else:
value = value.ljust(self._masklength)
# Record current selection and insertion point, for undo
self._prevSelection = self._GetSelection()
self._prevInsertionPoint = self._GetInsertionPoint()
wx.ComboBox.SetValue(self, value)
# text change events don't always fire, so we check validity here
# to make certain formatting is applied:
self._CheckValid()
def SetValue(self, value):
"""
This function redefines the externally accessible .SetValue to be
a smart "paste" of the text in question, so as not to corrupt the
masked control. NOTE: this must be done in the class derived
from the base wx control.
"""
if not self._mask:
wx.ComboBox.SetValue(value) # revert to base control behavior
return
# else...
# empty previous contents, replacing entire value:
self._SetInsertionPoint(0)
self._SetSelection(0, self._masklength)
if( len(value) < self._masklength # value shorter than control
and (self._isFloat or self._isInt) # and it's a numeric control
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)
# For wxComboBox, ensure that values are properly padded so that
# if varying length choices are supplied, they always show up
# in the window properly, and will be the appropriate length
# to match the mask:
elif self._ctrl_constraints._alignRight:
value = value.rjust(self._masklength)
else:
value = value.ljust(self._masklength)
# make SetValue behave the same as if you had typed the value in:
try:
value = self._Paste(value, raise_on_invalid=True, just_return_value=True)
if self._isFloat:
self._isNeg = False # (clear current assumptions)
value = self._adjustFloat(value)
elif self._isInt:
self._isNeg = False # (clear current assumptions)
value = self._adjustInt(value)
elif self._isDate and not self.IsValid(value) and self._4digityear:
value = self._adjustDate(value, fixcentury=True)
except ValueError:
# If date, year might be 2 digits vs. 4; try adjusting it:
if self._isDate and self._4digityear:
dateparts = value.split(' ')
dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True)
value = string.join(dateparts, ' ')
## 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)
wx.CallAfter(self._SetInsertionPoint, self._masklength)
wx.CallAfter(self._SetSelection, self._masklength, self._masklength)
def _Refresh(self):
"""
Allow mixin to refresh the base control with this function.
REQUIRED by any class derived from MaskedEditMixin.
"""
wx.ComboBox.Refresh(self)
def Refresh(self):
"""
This function redefines the externally accessible .Refresh() to
validate the contents of the masked control as it refreshes.
NOTE: this must be done in the class derived from the base wx control.
"""
self._CheckValid()
self._Refresh()
def _IsEditable(self):
"""
Allow mixin to determine if the base control is editable with this function.
REQUIRED by any class derived from MaskedEditMixin.
"""
return not self.__readonly
def Cut(self):
"""
This function redefines the externally accessible .Cut to be
a smart "erase" of the text in question, so as not to corrupt the
masked control. NOTE: this must be done in the class derived
from the base wx control.
"""
if self._mask:
self._Cut() # call the mixin's Cut method
else:
wx.ComboBox.Cut(self) # else revert to base control behavior
def Paste(self):
"""
This function redefines the externally accessible .Paste to be
a smart "paste" of the text in question, so as not to corrupt the
masked control. NOTE: this must be done in the class derived
from the base wx control.
"""
if self._mask:
self._Paste() # call the mixin's Paste method
else:
wx.ComboBox.Paste(self) # else revert to base control behavior
def Undo(self):
"""
This function defines the undo operation for the control. (The default
undo is 1-deep.)
"""
if self._mask:
self._Undo()
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
of choices, because wxComboBox doesn't have an accessor for the choice list.
The code here is the same as in the SetParameters() mixin function, but is
done for the individual value as appended, so the list can be built incrementally
without speed penalty.
"""
if self._mask:
if type(choice) not in (types.StringType, types.UnicodeType):
raise TypeError('%s: choices must be a sequence of strings' % str(self._index))
elif not self.IsValid(choice):
raise ValueError('%s: "%s" is not a valid value for the control as specified.' % (str(self._index), choice))
if not self._ctrl_constraints._choices:
self._ctrl_constraints._compareChoices = []
self._ctrl_constraints._choices = []
self._hasList = True
compareChoice = choice.strip()
if self._ctrl_constraints._compareNoCase:
compareChoice = compareChoice.lower()
if self._ctrl_constraints._alignRight:
choice = choice.rjust(self._masklength)
else:
choice = choice.ljust(self._masklength)
if self._ctrl_constraints._fillChar != ' ':
choice = choice.replace(' ', self._fillChar)
## dbg('updated choice:', choice)
self._ctrl_constraints._compareChoices.append(compareChoice)
self._ctrl_constraints._choices.append(choice)
self._choices = self._ctrl_constraints._choices # (for shorthand)
if( not self.IsValid(choice) and
(not self._ctrl_constraints.IsEmpty(choice) or
(self._ctrl_constraints.IsEmpty(choice) and self._ctrl_constraints._validRequired) ) ):
raise ValueError('"%s" is not a valid value for the control "%s" as specified.' % (choice, self.name))
wx.ComboBox.Append(self, choice, clientData)
def Clear( self ):
"""
This function override is necessary so we can keep track of any additions to the list
of choices, because wxComboBox doesn't have an accessor for the choice list.
"""
if self._mask:
self._choices = []
self._ctrl_constraints._autoCompleteIndex = -1
if self._ctrl_constraints._choices:
self.SetCtrlParameters(choices=[])
wx.ComboBox.Clear(self)
def _OnCtrlParametersChanged(self):
"""
Override mixin's default OnCtrlParametersChanged to detect changes in choice list, so
we can update the base control:
"""
if self.controlInitialized and self._choices != self._ctrl_constraints._choices:
wx.ComboBox.Clear(self)
self._choices = self._ctrl_constraints._choices
for choice in self._choices:
wx.ComboBox.Append( self, choice )
def GetMark(self):
"""
This function is a hack to make up for the fact that wxComboBox has no
method for returning the selected portion of its edit control. It
works, but has the nasty side effect of generating lots of intermediate
events.
"""
## dbg(suspend=1) # turn off debugging around this function
## dbg('MaskedComboBox::GetMark', indent=1)
if self.__readonly:
## 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)
value = self.GetValue()
## dbg('value: "%s"' % value)
self._ignoreChange = True # tell _OnTextChange() to ignore next event (if any)
wx.ComboBox.Cut(self)
newvalue = self.GetValue()
## dbg("value after Cut operation:", newvalue)
if newvalue != value: # something was selected; calculate extent
## dbg("something selected")
sel_to = sel_start + len(value) - len(newvalue)
wx.ComboBox.SetValue(self, value) # restore original value and selection (still ignoring change)
wx.ComboBox.SetInsertionPoint(self, sel_start)
wx.ComboBox.SetMark(self, sel_start, sel_to)
self._ignoreChange = False # tell _OnTextChange() to pay attn again
## dbg('computed selection:', sel_start, sel_to, indent=0, suspend=0)
return sel_start, sel_to
def SetSelection(self, index):
"""
Necessary for bookkeeping on choice selection, to keep current value
current.
"""
## dbg('MaskedComboBox::SetSelection(%d)' % index)
if self._mask:
self._prevValue = self._curValue
self._curValue = self._choices[index]
self._ctrl_constraints._autoCompleteIndex = index
wx.ComboBox.SetSelection(self, index)
def OnKeyDown(self, event):
"""
This function is necessary because navigation and control key
events do not seem to normally be seen by the wxComboBox's
EVT_CHAR routine. (Tabs don't seem to be visible no matter
what... {:-( )
"""
if event.GetKeyCode() in self._nav + self._control:
self._OnChar(event)
return
else:
event.Skip() # let mixin default KeyDown behavior occur
def OnSelectChoice(self, event):
"""
This function appears to be necessary, because the processing done
on the text of the control somehow interferes with the combobox's
selection mechanism for the arrow keys.
"""
## dbg('MaskedComboBox::OnSelectChoice', indent=1)
if not self._mask:
event.Skip()
return
value = self.GetValue().strip()
if self._ctrl_constraints._compareNoCase:
value = value.lower()
if event.GetKeyCode() == wx.WXK_UP:
direction = -1
else:
direction = 1
match_index, partial_match = self._autoComplete(
direction,
self._ctrl_constraints._compareChoices,
value,
self._ctrl_constraints._compareNoCase,
current_index = self._ctrl_constraints._autoCompleteIndex)
if match_index is not None:
## dbg('setting selection to', match_index)
# issue appropriate event to outside:
self._OnAutoSelect(self._ctrl_constraints, match_index=match_index)
self._CheckValid()
keep_processing = False
else:
pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
field = self._FindField(pos)
if self.IsEmpty() or not field._hasList:
## 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)
keep_processing = self._OnAutoCompleteField(event)
## dbg('keep processing?', keep_processing, indent=0)
return keep_processing
def _OnAutoSelect(self, field, match_index):
"""
Override mixin (empty) autocomplete handler, so that autocompletion causes
combobox to update appropriately.
"""
## dbg('MaskedComboBox::OnAutoSelect', field._index, indent=1)
## field._autoCompleteIndex = match_index
if field == self._ctrl_constraints:
self.SetSelection(match_index)
## dbg('issuing combo selection event')
self.GetEventHandler().ProcessEvent(
MaskedComboBoxSelectEvent( self.GetId(), match_index, self ) )
self._CheckValid()
## dbg('field._autoCompleteIndex:', match_index)
## dbg('self.GetSelection():', self.GetSelection())
## dbg(indent=0)
def _OnReturn(self, event):
"""
For wxComboBox, it seems that if you hit return when the dropdown is
dropped, the event that dismisses the dropdown will also blank the
control, because of the implementation of wxComboBox. So here,
we look and if the selection is -1, and the value according to
(the base control!) is a value in the list, then we schedule a
programmatic wxComboBox.SetSelection() call to pick the appropriate
item in the list. (and then do the usual OnReturn bit.)
"""
## dbg('MaskedComboBox::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:
wx.CallAfter(self.SetSelection, self._ctrl_constraints._autoCompleteIndex)
event.m_keyCode = wx.WXK_TAB
event.Skip()
## dbg(indent=0)
class ComboBox( BaseMaskedComboBox, MaskedEditAccessorsMixin ):
"""
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

View File

@ -1,5 +1,5 @@
#----------------------------------------------------------------------------
# Name: wxPython.lib.maskedctrl.py
# Name: wxPython.lib.masked.ctrl.py
# Author: Will Sadkin
# Created: 09/24/2003
# Copyright: (c) 2003 by Will Sadkin
@ -9,32 +9,32 @@
# 12/09/2003 - Jeff Grimmett (grimmtooth@softhome.net)
#
# o Updated for wx namespace (minor)
#
#
# 12/20/2003 - Jeff Grimmett (grimmtooth@softhome.net)
#
# o Removed wx prefix
#
#
"""<html><body>
<P>
<B>MaskedCtrl</B> is actually a factory function for several types of
<B>masked.Ctrl</B> is actually a factory function for several types of
masked edit controls:
<P>
<UL>
<LI><b>MaskedTextCtrl</b> - standard masked edit text box</LI>
<LI><b>MaskedComboBox</b> - adds combobox capabilities</LI>
<LI><b>IpAddrCtrl</b> - adds logical input semantics for IP address entry</LI>
<LI><b>TimeCtrl</b> - special subclass handling lots of time formats as values</LI>
<LI><b>MaskedNumCtrl</b> - special subclass handling numeric values</LI>
<LI><b>masked.TextCtrl</b> - standard masked edit text box</LI>
<LI><b>masked.ComboBox</b> - adds combobox capabilities</LI>
<LI><b>masked.IpAddrCtrl</b> - adds logical input semantics for IP address entry</LI>
<LI><b>masked.TimeCtrl</b> - special subclass handling lots of time formats as values</LI>
<LI><b>masked.NumCtrl</b> - special subclass handling numeric values</LI>
</UL>
<P>
<B>MaskedCtrl</B> works by looking for a special <b><i>controlType</i></b>
<B>masked.Ctrl</B> works by looking for a special <b><i>controlType</i></b>
parameter in the variable arguments of the control, to determine
what kind of instance to return.
controlType can be one of:
<PRE><FONT SIZE=-1>
controlTypes.MASKEDTEXT
controlTypes.MASKEDCOMBO
controlTypes.TEXT
controlTypes.COMBO
controlTypes.IPADDR
controlTypes.TIME
controlTypes.NUMBER
@ -42,56 +42,56 @@ controlType can be one of:
These constants are also available individually, ie, you can
use either of the following:
<PRE><FONT SIZE=-1>
from wxPython.wx.lib.maskedctrl import MaskedCtrl, MASKEDCOMBO, MASKEDTEXT, NUMBER
from wxPython.wx.lib.maskedctrl import MaskedCtrl, controlTypes
from wxPython.wx.lib.masked import Ctrl, COMBO, TEXT, NUMBER, TIME
from wxPython.wx.lib.masked import Ctrl, controlTypes
</FONT></PRE>
If not specified as a keyword argument, the default controlType is
controlTypes.MASKEDTEXT.
controlTypes.TEXT.
<P>
Each of the above classes has its own unique arguments, but MaskedCtrl
provides a single "unified" interface for masked controls. MaskedTextCtrl,
MaskedComboBox and IpAddrCtrl are all documented below; the others have
provides a single "unified" interface for masked controls. Masked.TextCtrl,
masked.ComboBox and masked.IpAddrCtrl are all documented below; the others have
their own demo pages and interface descriptions.
</body></html>
"""
from wx.lib.maskededit import MaskedTextCtrl, MaskedComboBox, IpAddrCtrl
from wx.lib.maskednumctrl import MaskedNumCtrl
from wx.lib.timectrl import TimeCtrl
from wx.lib.masked import TextCtrl, ComboBox, IpAddrCtrl
from wx.lib.masked import NumCtrl
from wx.lib.masked import TimeCtrl
# "type" enumeration for class instance factory function
MASKEDTEXT = 0
MASKEDCOMBO = 1
TEXT = 0
COMBO = 1
IPADDR = 2
TIME = 3
NUMBER = 4
# for ease of import
class controlTypes:
MASKEDTEXT = MASKEDTEXT
MASKEDCOMBO = MASKEDCOMBO
TEXT = TEXT
COMBO = COMBO
IPADDR = IPADDR
TIME = TIME
NUMBER = NUMBER
def MaskedCtrl( *args, **kwargs):
def Ctrl( *args, **kwargs):
"""
Actually a factory function providing a unifying
interface for generating masked controls.
"""
if not kwargs.has_key('controlType'):
controlType = MASKEDTEXT
controlType = TEXT
else:
controlType = kwargs['controlType']
del kwargs['controlType']
if controlType == MASKEDTEXT:
return MaskedTextCtrl(*args, **kwargs)
if controlType == TEXT:
return TextCtrl(*args, **kwargs)
elif controlType == MASKEDCOMBO:
return MaskedComboBox(*args, **kwargs)
elif controlType == COMBO:
return ComboBox(*args, **kwargs)
elif controlType == IPADDR:
return IpAddrCtrl(*args, **kwargs)
@ -100,7 +100,7 @@ def MaskedCtrl( *args, **kwargs):
return TimeCtrl(*args, **kwargs)
elif controlType == NUMBER:
return MaskedNumCtrl(*args, **kwargs)
return NumCtrl(*args, **kwargs)
else:
raise AttributeError(

View File

@ -0,0 +1,187 @@
#----------------------------------------------------------------------------
# Name: masked.ipaddrctrl.py
# Authors: Will Sadkin
# Email: wsadkin@nameconnector.com
# Created: 02/11/2003
# Copyright: (c) 2003 by Will Sadkin, 2003
# RCS-ID: $Id$
# License: wxWidgets license
#----------------------------------------------------------------------------
# NOTE:
# Masked.IpAddrCtrl is a minor modification to masked.TextCtrl, that is
# specifically tailored for entering IP addresses. It allows for
# right-insert fields and provides an accessor to obtain the entered
# address with extra whitespace removed.
#
#----------------------------------------------------------------------------
import wx
from wx.lib.masked import BaseMaskedTextCtrl
# jmg 12/9/03 - when we cut ties with Py 2.2 and earlier, this would
# be a good place to implement the 2.3 logger class
from wx.tools.dbg import Logger
dbg = Logger()
##dbg(enable=0)
class IpAddrCtrlAccessorsMixin:
# Define IpAddrCtrl'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 IpAddrCtrl( BaseMaskedTextCtrl, IpAddrCtrlAccessorsMixin ):
"""
This class is a particular type of MaskedTextCtrl 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 = wx.DefaultPosition,
size = wx.DefaultSize,
style = wx.TE_PROCESS_TAB,
validator = wx.DefaultValidator,
name = 'IpAddrCtrl',
setupEventHandling = True, ## setup event handling by default
**kwargs):
if not kwargs.has_key('mask'):
kwargs['mask'] = mask = "###.###.###.###"
if not kwargs.has_key('formatcodes'):
kwargs['formatcodes'] = 'F_Sr<'
if not kwargs.has_key('validRegex'):
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}"
BaseMaskedTextCtrl.__init__(
self, parent, id=id, value = value,
pos=pos, size=size,
style = style,
validator = validator,
name = name,
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]))"
# require "valid" string; this prevents entry of any value > 255, but allows
# intermediate constructions; overall control validation requires well-formatted value.
field_params['formatcodes'] = 'V'
if field_params:
for i in self._field_indices:
self.SetFieldParameters(i, **field_params)
# This makes '.' act like tab:
self._AddNavKey('.', handler=self.OnDot)
self._AddNavKey('>', handler=self.OnDot) # for "shift-."
def OnDot(self, event):
## dbg('IpAddrCtrl::OnDot', indent=1)
pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
oldvalue = self.GetValue()
edit_start, edit_end, slice = self._FindFieldExtent(pos, getslice=True)
if not event.ShiftDown():
if pos > edit_start and pos < edit_end:
# clip data in field to the right of pos, if adjusting fields
# when not at delimeter; (assumption == they hit '.')
newvalue = oldvalue[:pos] + ' ' * (edit_end - pos) + oldvalue[edit_end:]
self._SetValue(newvalue)
self._SetInsertionPoint(pos)
## dbg(indent=0)
return self._OnChangeField(event)
def GetAddress(self):
value = BaseMaskedTextCtrl.GetValue(self)
return value.replace(' ','') # remove spaces from the value
def _OnCtrl_S(self, event):
## dbg("IpAddrCtrl::_OnCtrl_S")
if self._demo:
print "value:", self.GetAddress()
return False
def SetValue(self, value):
## dbg('IpAddrCtrl::SetValue(%s)' % str(value), indent=1)
if type(value) not in (types.StringType, types.UnicodeType):
## dbg(indent=0)
raise ValueError('%s must be a string', str(value))
bValid = True # assume True
parts = value.split('.')
if len(parts) != 4:
bValid = False
else:
for i in range(4):
part = parts[i]
if not 0 <= len(part) <= 3:
bValid = False
break
elif part.strip(): # non-empty part
try:
j = string.atoi(part)
if not 0 <= j <= 255:
bValid = False
break
else:
parts[i] = '%3d' % j
except:
bValid = False
break
else:
# allow empty sections for SetValue (will result in "invalid" value,
# but this may be useful for initializing the control:
parts[i] = ' ' # convert empty field to 3-char length
if not bValid:
## 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)
value = string.join(parts, '.')
BaseMaskedTextCtrl.SetValue(self, value)
## dbg(indent=0)

View File

@ -1,5 +1,5 @@
#----------------------------------------------------------------------------
# Name: wxPython.lib.maskednumctrl.py
# Name: wxPython.lib.masked.numctrl.py
# Author: Will Sadkin
# Created: 09/06/2003
# Copyright: (c) 2003 by Will Sadkin
@ -9,12 +9,12 @@
# NOTE:
# This was written to provide a numeric edit control for wxPython that
# does things like right-insert (like a calculator), and does grouping, etc.
# (ie. the features of MaskedTextCtrl), but allows Get/Set of numeric
# (ie. the features of masked.TextCtrl), but allows Get/Set of numeric
# values, rather than text.
#
# MaskedNumCtrl permits integer, and floating point values to be set
# Masked.NumCtrl permits integer, and floating point values to be set
# retrieved or set via .GetValue() and .SetValue() (type chosen based on
# fraction width, and provides an EVT_MASKEDNUM() event function for trapping
# fraction width, and provides an masked.EVT_NUM() event function for trapping
# changes to the control.
#
# It supports negative numbers as well as the naturals, and has the option
@ -24,29 +24,29 @@
# Similarly, replacing the contents of the control with '-' will result in
# a selected (absolute) value of -1.
#
# MaskedNumCtrl also supports range limits, with the option of either
# masked.NumCtrl also supports range limits, with the option of either
# enforcing them or simply coloring the text of the control if the limits
# are exceeded.
#
# MaskedNumCtrl is intended to support fixed-point numeric entry, and
# masked.NumCtrl is intended to support fixed-point numeric entry, and
# 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)
#
# o Updated for wx namespace
#
#
# 12/20/2003 - Jeff Grimmett (grimmtooth@softhome.net)
#
# o wxMaskedEditMixin -> MaskedEditMixin
# o wxMaskedTextCtrl -> MaskedTextCtrl
# o wxMaskedNumNumberUpdatedEvent -> MaskedNumNumberUpdatedEvent
# o wxMaskedNumCtrl -> MaskedNumCtrl
# o wxMaskedTextCtrl -> masked.TextCtrl
# o wxMaskedNumNumberUpdatedEvent -> masked.NumberUpdatedEvent
# o wxMaskedNumCtrl -> masked.NumCtrl
#
"""<html><body>
<P>
<B>MaskedNumCtrl:</B>
<B>masked.NumCtrl:</B>
<UL>
<LI>allows you to get and set integer or floating point numbers as value,</LI>
<LI>provides bounds support and optional value limiting,</LI>
@ -62,14 +62,14 @@ fractional portion.
<P>
Here's the API:
<DL><PRE>
<B>MaskedNumCtrl</B>(
<B>masked.NumCtrl</B>(
parent, id = -1,
<B>value</B> = 0,
pos = wx.DefaultPosition,
size = wx.DefaultSize,
style = 0,
validator = wx.DefaultValidator,
name = "maskednumber",
name = "masked.number",
<B>integerWidth</B> = 10,
<B>fractionWidth</B> = 0,
<B>allowNone</B> = False,
@ -87,7 +87,7 @@ Here's the API:
<B>emptyBackgroundColour</B> = "White",
<B>validBackgroundColour</B> = "White",
<B>invalidBackgroundColour</B> = "Yellow",
<B>autoSize</B> = True
<B>autoSize</B> = True
)
</PRE>
<UL>
@ -190,7 +190,7 @@ Here's the API:
</UL>
<BR>
<BR>
<DT><B>EVT_MASKEDNUM(win, id, func)</B>
<DT><B>masked.EVT_NUM(win, id, func)</B>
<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
@ -384,18 +384,18 @@ MAXINT = maxint # (constants should be in upper case)
MININT = -maxint-1
from wx.tools.dbg import Logger
from wx.lib.maskededit import MaskedEditMixin, BaseMaskedTextCtrl, Field
from wx.lib.masked import MaskedEditMixin, Field, BaseMaskedTextCtrl
dbg = Logger()
dbg(enable=0)
##dbg(enable=0)
#----------------------------------------------------------------------------
wxEVT_COMMAND_MASKED_NUMBER_UPDATED = wx.NewEventType()
EVT_MASKEDNUM = wx.PyEventBinder(wxEVT_COMMAND_MASKED_NUMBER_UPDATED, 1)
EVT_NUM = wx.PyEventBinder(wxEVT_COMMAND_MASKED_NUMBER_UPDATED, 1)
#----------------------------------------------------------------------------
class MaskedNumNumberUpdatedEvent(wx.PyCommandEvent):
class NumberUpdatedEvent(wx.PyCommandEvent):
def __init__(self, id, value = 0, object=None):
wx.PyCommandEvent.__init__(self, wxEVT_COMMAND_MASKED_NUMBER_UPDATED, id)
@ -409,8 +409,8 @@ class MaskedNumNumberUpdatedEvent(wx.PyCommandEvent):
#----------------------------------------------------------------------------
class MaskedNumCtrlAccessorsMixin:
# Define wxMaskedNumCtrl's list of attributes having their own
class NumCtrlAccessorsMixin:
# Define masked.NumCtrl's list of attributes having their own
# Get/Set functions, ignoring those that make no sense for
# an numeric control.
exposed_basectrl_params = (
@ -447,8 +447,8 @@ class MaskedNumCtrlAccessorsMixin:
#----------------------------------------------------------------------------
class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
valid_ctrl_params = {
@ -470,7 +470,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
'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
'autoSize': True, # by default, set the width of the control based on the mask
}
@ -478,31 +478,32 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
self, parent, id=-1, value = 0,
pos = wx.DefaultPosition, size = wx.DefaultSize,
style = wx.TE_PROCESS_TAB, validator = wx.DefaultValidator,
name = "maskednum",
name = "masked.num",
**kwargs ):
dbg('MaskedNumCtrl::__init__', indent=1)
## dbg('masked.NumCtrl::__init__', indent=1)
# Set defaults for control:
dbg('setting defaults:')
for key, param_value in MaskedNumCtrl.valid_ctrl_params.items():
## dbg('setting defaults:')
for key, param_value in NumCtrl.valid_ctrl_params.items():
# This is done this way to make setattr behave consistently with
# "private attribute" name mangling
setattr(self, '_' + key, copy.copy(param_value))
# Assign defaults for all attributes:
init_args = copy.deepcopy(MaskedNumCtrl.valid_ctrl_params)
dbg('kwargs:', kwargs)
init_args = copy.deepcopy(NumCtrl.valid_ctrl_params)
## dbg('kwargs:', kwargs)
for key, param_value in kwargs.items():
key = key.replace('Color', 'Colour')
if key not in MaskedNumCtrl.valid_ctrl_params.keys():
if key not in NumCtrl.valid_ctrl_params.keys():
raise AttributeError('invalid keyword argument "%s"' % key)
else:
init_args[key] = param_value
dbg('init_args:', indent=1)
## dbg('init_args:', indent=1)
for key, param_value in init_args.items():
dbg('%s:' % key, param_value)
dbg(indent=0)
## dbg('%s:' % key, param_value)
pass
## dbg(indent=0)
# Process initial fields for the control, as part of construction:
if type(init_args['integerWidth']) != types.IntType:
@ -521,7 +522,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
if self._fractionWidth:
fracmask = '.' + '#{%d}' % self._fractionWidth
dbg('fracmask:', fracmask)
## dbg('fracmask:', fracmask)
fields[1] = Field(defaultValue='0'*self._fractionWidth)
else:
fracmask = ''
@ -537,7 +538,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
else:
emptyInvalid = True
fields[0] = Field(formatcodes='r<>', emptyInvalid=emptyInvalid)
dbg('intmask:', intmask)
## dbg('intmask:', intmask)
# don't bother to reprocess these arguments:
del init_args['integerWidth']
@ -585,14 +586,14 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
# Ensure proper coloring:
self.Refresh()
dbg('finished MaskedNumCtrl::__init__', indent=0)
## dbg('finished NumCtrl::__init__', indent=0)
def SetParameters(self, **kwargs):
"""
This routine is used to initialize and reconfigure the control:
"""
dbg('MaskedNumCtrl::SetParameters', indent=1)
## dbg('NumCtrl::SetParameters', indent=1)
maskededit_kwargs = {}
reset_fraction_width = False
@ -620,14 +621,14 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
else:
emptyInvalid = True
fracmask = ''
dbg('fracmask:', fracmask)
## dbg('fracmask:', fracmask)
if kwargs.has_key('integerWidth'):
if type(kwargs['integerWidth']) != types.IntType:
dbg(indent=0)
## dbg(indent=0)
raise AttributeError('invalid integerWidth (%s) specified; expected integer' % repr(kwargs['integerWidth']))
elif kwargs['integerWidth'] < 0:
dbg(indent=0)
## dbg(indent=0)
raise AttributeError('invalid integerWidth (%s) specified; must be > 0' % repr(kwargs['integerWidth']))
else:
self._integerWidth = kwargs['integerWidth']
@ -641,7 +642,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
self._groupSpace = 0
intmask = '#{%d}' % (self._integerWidth + self._groupSpace)
dbg('intmask:', intmask)
## dbg('intmask:', intmask)
fields[0] = Field(formatcodes='r<>', emptyInvalid=emptyInvalid)
maskededit_kwargs['fields'] = fields
@ -655,17 +656,17 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
if kwargs.has_key('groupChar'):
old_groupchar = self._groupChar # save so we can reformat properly
dbg("old_groupchar: '%s'" % old_groupchar)
## dbg("old_groupchar: '%s'" % old_groupchar)
maskededit_kwargs['groupChar'] = kwargs['groupChar']
if kwargs.has_key('decimalChar'):
old_decimalchar = self._decimalChar
dbg("old_decimalchar: '%s'" % old_decimalchar)
## dbg("old_decimalchar: '%s'" % old_decimalchar)
maskededit_kwargs['decimalChar'] = kwargs['decimalChar']
# for all other parameters, assign keyword args as appropriate:
for key, param_value in kwargs.items():
key = key.replace('Color', 'Colour')
if key not in MaskedNumCtrl.valid_ctrl_params.keys():
if key not in NumCtrl.valid_ctrl_params.keys():
raise AttributeError('invalid keyword argument "%s"' % key)
elif key not in MaskedEditMixin.valid_ctrl_params.keys():
setattr(self, '_' + key, param_value)
@ -673,10 +674,10 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
raise AttributeError('invalid keyword argument "%s"' % key)
else:
maskededit_kwargs[key] = param_value
dbg('kwargs:', kwargs)
## dbg('kwargs:', kwargs)
# reprocess existing format codes to ensure proper resulting format:
formatcodes = self.GetCtrlParameter('formatcodes')
formatcodes = self.GetCtrlParameter('formatcodes')
if kwargs.has_key('allowNegative'):
if kwargs['allowNegative'] and '-' not in formatcodes:
formatcodes += '-'
@ -695,7 +696,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
if kwargs.has_key('selectOnEntry'):
self._selectOnEntry = kwargs['selectOnEntry']
dbg("kwargs['selectOnEntry']?", kwargs['selectOnEntry'], "'S' in formatcodes?", 'S' in formatcodes)
## dbg("kwargs['selectOnEntry']?", kwargs['selectOnEntry'], "'S' in formatcodes?", 'S' in formatcodes)
if kwargs['selectOnEntry'] and 'S' not in formatcodes:
formatcodes += 'S'
maskededit_kwargs['formatcodes'] = formatcodes
@ -728,7 +729,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
maskededit_kwargs['validRequired'] = False
self._limited = kwargs['limited']
dbg('maskededit_kwargs:', maskededit_kwargs)
## dbg('maskededit_kwargs:', maskededit_kwargs)
if maskededit_kwargs.keys():
self.SetCtrlParameters(**maskededit_kwargs)
@ -756,17 +757,18 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
if( self._max is None
or min is None
or (self._max is not None and self._max >= min) ):
dbg('examining min')
## dbg('examining min')
if min is not None:
try:
textmin = self._toGUI(min, apply_limits = False)
except ValueError:
dbg('min will not fit into control; ignoring', indent=0)
## dbg('min will not fit into control; ignoring', indent=0)
raise
dbg('accepted min')
## dbg('accepted min')
self._min = min
else:
dbg('ignoring min')
## dbg('ignoring min')
pass
if kwargs.has_key('max'):
@ -774,24 +776,25 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
if( self._min is None
or max is None
or (self._min is not None and self._min <= max) ):
dbg('examining max')
## dbg('examining max')
if max is not None:
try:
textmax = self._toGUI(max, apply_limits = False)
except ValueError:
dbg('max will not fit into control; ignoring', indent=0)
## dbg('max will not fit into control; ignoring', indent=0)
raise
dbg('accepted max')
## dbg('accepted max')
self._max = max
else:
dbg('ignoring max')
## dbg('ignoring max')
pass
if kwargs.has_key('allowNegative'):
self._allowNegative = kwargs['allowNegative']
# Ensure current value of control obeys any new restrictions imposed:
text = self._GetValue()
dbg('text value: "%s"' % text)
## dbg('text value: "%s"' % text)
if kwargs.has_key('groupChar') and text.find(old_groupchar) != -1:
text = text.replace(old_groupchar, self._groupChar)
if kwargs.has_key('decimalChar') and text.find(old_decimalchar) != -1:
@ -801,10 +804,10 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
value = self.GetValue()
dbg('self._allowNegative?', self._allowNegative)
## dbg('self._allowNegative?', self._allowNegative)
if not self._allowNegative and self._isNeg:
value = abs(value)
dbg('abs(value):', value)
## dbg('abs(value):', value)
self._isNeg = False
elif not self._allowNone and BaseMaskedTextCtrl.GetValue(self) == '':
@ -815,19 +818,19 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
sel_start, sel_to = self.GetSelection()
if self.IsLimited() and self._min is not None and value < self._min:
dbg('Set to min value:', self._min)
## dbg('Set to min value:', self._min)
self._SetValue(self._toGUI(self._min))
elif self.IsLimited() and self._max is not None and value > self._max:
dbg('Setting to max value:', self._max)
## dbg('Setting to max value:', self._max)
self._SetValue(self._toGUI(self._max))
else:
# reformat current value as appropriate to possibly new conditions
dbg('Reformatting value:', value)
## dbg('Reformatting value:', value)
sel_start, sel_to = self.GetSelection()
self._SetValue(self._toGUI(value))
self.Refresh() # recolor as appropriate
dbg('finished MaskedNumCtrl::SetParameters', indent=0)
## dbg('finished NumCtrl::SetParameters', indent=0)
@ -859,21 +862,21 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
return string.atof(fracstring)
def _OnChangeSign(self, event):
dbg('MaskedNumCtrl::_OnChangeSign', indent=1)
## dbg('NumCtrl::_OnChangeSign', indent=1)
self._typedSign = True
MaskedEditMixin._OnChangeSign(self, event)
dbg(indent=0)
## dbg(indent=0)
def _disallowValue(self):
dbg('MaskedNumCtrl::_disallowValue')
## dbg('NumCtrl::_disallowValue')
# limited and -1 is out of bounds
if self._typedSign:
self._isNeg = False
if not wx.Validator_IsSilent():
wx.Bell()
sel_start, sel_to = self._GetSelection()
dbg('queuing reselection of (%d, %d)' % (sel_start, sel_to))
## dbg('queuing reselection of (%d, %d)' % (sel_start, sel_to))
wx.CallAfter(self.SetInsertionPoint, sel_start) # preserve current selection/position
wx.CallAfter(self.SetSelection, sel_start, sel_to)
@ -886,19 +889,19 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
by the user.
"""
dbg('MaskedNumCtrl::_SetValue("%s")' % value, indent=1)
## dbg('NumCtrl::_SetValue("%s")' % value, indent=1)
if( (self._fractionWidth and value.find(self._decimalChar) == -1) or
(self._fractionWidth == 0 and value.find(self._decimalChar) != -1) ) :
value = self._toGUI(value)
numvalue = self._GetNumValue(value)
dbg('cleansed value: "%s"' % numvalue)
## dbg('cleansed value: "%s"' % numvalue)
replacement = None
if numvalue == "":
if self._allowNone:
dbg('calling base BaseMaskedTextCtrl._SetValue(self, "%s")' % value)
## dbg('calling base BaseMaskedTextCtrl._SetValue(self, "%s")' % value)
BaseMaskedTextCtrl._SetValue(self, value)
self.Refresh()
return
@ -906,54 +909,54 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
replacement = self._min
else:
replacement = 0
dbg('empty value; setting replacement:', replacement)
## dbg('empty value; setting replacement:', replacement)
if replacement is None:
# Go get the integer portion about to be set and verify its validity
intstart, intend = self._fields[0]._extent
dbg('intstart, intend:', intstart, intend)
dbg('raw integer:"%s"' % value[intstart:intend])
## dbg('intstart, intend:', intstart, intend)
## dbg('raw integer:"%s"' % value[intstart:intend])
int = self._GetNumValue(value[intstart:intend])
numval = self._fromGUI(value)
dbg('integer: "%s"' % int)
## dbg('integer: "%s"' % int)
try:
fracval = self.GetFraction(value)
except ValueError, e:
dbg('Exception:', e, 'must be out of bounds; disallow value')
## dbg('Exception:', e, 'must be out of bounds; disallow value')
self._disallowValue()
dbg(indent=0)
## dbg(indent=0)
return
if fracval == 0.0:
dbg('self._isNeg?', self._isNeg)
## dbg('self._isNeg?', self._isNeg)
if int == '-' and self._oldvalue < 0 and not self._typedSign:
dbg('just a negative sign; old value < 0; setting replacement of 0')
## dbg('just a negative sign; old value < 0; setting replacement of 0')
replacement = 0
self._isNeg = False
elif int[:2] == '-0' and self._fractionWidth == 0:
if self._oldvalue < 0:
dbg('-0; setting replacement of 0')
## dbg('-0; setting replacement of 0')
replacement = 0
self._isNeg = False
elif not self._limited or (self._min < -1 and self._max >= -1):
dbg('-0; setting replacement of -1')
## dbg('-0; setting replacement of -1')
replacement = -1
self._isNeg = True
else:
# limited and -1 is out of bounds
self._disallowValue()
dbg(indent=0)
## dbg(indent=0)
return
elif int == '-' and (self._oldvalue >= 0 or self._typedSign) and self._fractionWidth == 0:
if not self._limited or (self._min < -1 and self._max >= -1):
dbg('just a negative sign; setting replacement of -1')
## dbg('just a negative sign; setting replacement of -1')
replacement = -1
else:
# limited and -1 is out of bounds
self._disallowValue()
dbg(indent=0)
## dbg(indent=0)
return
elif( self._typedSign
@ -963,7 +966,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
# changed sign resulting in value that's now out-of-bounds;
# disallow
self._disallowValue()
dbg(indent=0)
## dbg(indent=0)
return
if replacement is None:
@ -974,37 +977,37 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
# integer requested is not legal. This can happen if the user
# is attempting to insert a digit in the middle of the control
# resulting in something like " 3 45". Disallow such actions:
dbg('>>>>>>>>>>>>>>>> "%s" does not convert to a long!' % int)
## dbg('>>>>>>>>>>>>>>>> "%s" does not convert to a long!' % int)
if not wx.Validator_IsSilent():
wx.Bell()
sel_start, sel_to = self._GetSelection()
dbg('queuing reselection of (%d, %d)' % (sel_start, sel_to))
## dbg('queuing reselection of (%d, %d)' % (sel_start, sel_to))
wx.CallAfter(self.SetInsertionPoint, sel_start) # preserve current selection/position
wx.CallAfter(self.SetSelection, sel_start, sel_to)
dbg(indent=0)
## dbg(indent=0)
return
if int[0] == '0' and len(int) > 1:
dbg('numvalue: "%s"' % numvalue.replace(' ', ''))
## dbg('numvalue: "%s"' % numvalue.replace(' ', ''))
if self._fractionWidth:
value = self._toGUI(string.atof(numvalue))
else:
value = self._toGUI(string.atol(numvalue))
dbg('modified value: "%s"' % value)
## dbg('modified value: "%s"' % value)
self._typedSign = False # reset state var
if replacement is not None:
# Value presented wasn't a legal number, but control should do something
# reasonable instead:
dbg('setting replacement value:', replacement)
## dbg('setting replacement value:', replacement)
self._SetValue(self._toGUI(replacement))
sel_start = BaseMaskedTextCtrl.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))
## dbg('queuing selection of (%d, %d)' %(sel_start, sel_to))
wx.CallAfter(self.SetInsertionPoint, sel_start)
wx.CallAfter(self.SetSelection, sel_start, sel_to)
dbg(indent=0)
## dbg(indent=0)
return
# Otherwise, apply appropriate formatting to value:
@ -1016,35 +1019,35 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
else:
self._isNeg = False
dbg('value:"%s"' % value, 'self._useParens:', self._useParens)
## dbg('value:"%s"' % value, 'self._useParens:', self._useParens)
if self._fractionWidth:
adjvalue = self._adjustFloat(self._GetNumValue(value).replace('.',self._decimalChar))
else:
adjvalue = self._adjustInt(self._GetNumValue(value))
dbg('adjusted value: "%s"' % adjvalue)
## dbg('adjusted value: "%s"' % adjvalue)
sel_start, sel_to = self._GetSelection() # record current insertion point
dbg('calling BaseMaskedTextCtrl._SetValue(self, "%s")' % 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)
dbg('finished MaskedNumCtrl::_SetValue', indent=0)
## dbg('finished NumCtrl::_SetValue', indent=0)
def _CheckInsertionPoint(self):
# If current insertion point is before the end of the integer and
# its before the 1st digit, place it just after the sign position:
dbg('MaskedNumCtrl::CheckInsertionPoint', indent=1)
## dbg('NumCtrl::CheckInsertionPoint', indent=1)
sel_start, sel_to = self._GetSelection()
text = self._GetValue()
if sel_to < self._fields[0]._extent[1] and text[sel_to] in (' ', '-', '('):
text, signpos, right_signpos = self._getSignedValue()
dbg('setting selection(%d, %d)' % (signpos+1, signpos+1))
## dbg('setting selection(%d, %d)' % (signpos+1, signpos+1))
self.SetInsertionPoint(signpos+1)
self.SetSelection(signpos+1, signpos+1)
dbg(indent=0)
## dbg(indent=0)
def _OnErase( self, event ):
@ -1053,7 +1056,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
grouping characters auto selects the digit before or after the
grouping character, so that the erasure does the right thing.
"""
dbg('MaskedNumCtrl::_OnErase', indent=1)
## dbg('NumCtrl::_OnErase', indent=1)
#if grouping digits, make sure deletes next to group char always
# delete next digit to appropriate side:
@ -1086,21 +1089,21 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
self.SetSelection(sel_start, sel_to+1)
BaseMaskedTextCtrl._OnErase(self, event)
dbg(indent=0)
## dbg(indent=0)
def OnTextChange( self, event ):
"""
Handles an event indicating that the text control's value
has changed, and issue EVT_MaskedNum event.
has changed, and issue EVT_NUM event.
NOTE: using wxTextCtrl.SetValue() to change the control's
contents from within a EVT_CHAR handler can cause double
text events. So we check for actual changes to the text
before passing the events on.
"""
dbg('MaskedNumCtrl::OnTextChange', indent=1)
## dbg('NumCtrl::OnTextChange', indent=1)
if not BaseMaskedTextCtrl._OnTextChange(self, event):
dbg(indent=0)
## dbg(indent=0)
return
# else... legal value
@ -1109,14 +1112,14 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
if value != self._oldvalue:
try:
self.GetEventHandler().ProcessEvent(
MaskedNumNumberUpdatedEvent( self.GetId(), self.GetValue(), self ) )
NumberUpdatedEvent( self.GetId(), self.GetValue(), self ) )
except ValueError:
dbg(indent=0)
## dbg(indent=0)
return
# let normal processing of the text continue
event.Skip()
self._oldvalue = value # record for next event
dbg(indent=0)
## dbg(indent=0)
def _GetValue(self):
"""
@ -1172,7 +1175,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
If min > the max value allowed by the width of the control,
the function will return False, and the min will not be set.
"""
dbg('MaskedNumCtrl::SetMin(%s)' % repr(min), indent=1)
## dbg('NumCtrl::SetMin(%s)' % repr(min), indent=1)
if( self._max is None
or min is None
or (self._max is not None and self._max >= min) ):
@ -1183,7 +1186,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
bRet = False
else:
bRet = False
dbg(indent=0)
## dbg(indent=0)
return bRet
def GetMin(self):
@ -1286,14 +1289,14 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
also be called with a value to see if that value would fall within
the current bounds of the given control.
"""
dbg('IsInBounds(%s)' % repr(value), indent=1)
## dbg('IsInBounds(%s)' % repr(value), indent=1)
if value is None:
value = self.GetValue()
else:
try:
value = self._GetNumValue(self._toGUI(value))
except ValueError, e:
dbg('error getting NumValue(self._toGUI(value)):', e, indent=0)
## dbg('error getting NumValue(self._toGUI(value)):', e, indent=0)
return False
if value.strip() == '':
value = None
@ -1309,10 +1312,10 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
# if bounds set, and value is None, return False
if value == None and (min is not None or max is not None):
dbg('finished IsInBounds', indent=0)
## dbg('finished IsInBounds', indent=0)
return 0
else:
dbg('finished IsInBounds', indent=0)
## dbg('finished IsInBounds', indent=0)
return min <= value <= max
@ -1383,21 +1386,21 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
type and bounds checking and raises ValueError if argument is
not a valid value.
"""
dbg('MaskedNumCtrl::_toGUI(%s)' % repr(value), indent=1)
## dbg('NumCtrl::_toGUI(%s)' % repr(value), indent=1)
if value is None and self.IsNoneAllowed():
dbg(indent=0)
## dbg(indent=0)
return self._template
elif type(value) in (types.StringType, types.UnicodeType):
value = self._GetNumValue(value)
dbg('cleansed num value: "%s"' % value)
## dbg('cleansed num value: "%s"' % value)
if value == "":
if self.IsNoneAllowed():
dbg(indent=0)
## dbg(indent=0)
return self._template
else:
dbg('exception raised:', e, indent=0)
raise ValueError ('wxMaskedNumCtrl requires numeric value, passed %s'% repr(value) )
## dbg('exception raised:', e, indent=0)
raise ValueError ('NumCtrl requires numeric value, passed %s'% repr(value) )
# else...
try:
if self._fractionWidth or value.find('.') != -1:
@ -1405,13 +1408,13 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
else:
value = long(value)
except Exception, e:
dbg('exception raised:', e, indent=0)
raise ValueError ('MaskedNumCtrl requires numeric value, passed %s'% repr(value) )
## dbg('exception raised:', e, indent=0)
raise ValueError ('NumCtrl requires numeric value, passed %s'% repr(value) )
elif type(value) not in (types.IntType, types.LongType, types.FloatType):
dbg(indent=0)
## dbg(indent=0)
raise ValueError (
'MaskedNumCtrl requires numeric value, passed %s'% repr(value) )
'NumCtrl requires numeric value, passed %s'% repr(value) )
if not self._allowNegative and value < 0:
raise ValueError (
@ -1421,29 +1424,29 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
min = self.GetMin()
max = self.GetMax()
if not min is None and value < min:
dbg(indent=0)
## dbg(indent=0)
raise ValueError (
'value %d is below minimum value of control'% value )
if not max is None and value > max:
dbg(indent=0)
## dbg(indent=0)
raise ValueError (
'value %d exceeds value of control'% value )
adjustwidth = len(self._mask) - (1 * self._useParens * self._signOk)
dbg('len(%s):' % self._mask, len(self._mask))
dbg('adjustwidth - groupSpace:', adjustwidth - self._groupSpace)
dbg('adjustwidth:', adjustwidth)
## dbg('len(%s):' % self._mask, len(self._mask))
## dbg('adjustwidth - groupSpace:', adjustwidth - self._groupSpace)
## dbg('adjustwidth:', adjustwidth)
if self._fractionWidth == 0:
s = str(long(value)).rjust(self._integerWidth)
else:
format = '%' + '%d.%df' % (self._integerWidth+self._fractionWidth+1, self._fractionWidth)
s = format % float(value)
dbg('s:"%s"' % s, 'len(s):', len(s))
## dbg('s:"%s"' % s, 'len(s):', len(s))
if len(s) > (adjustwidth - self._groupSpace):
dbg(indent=0)
## dbg(indent=0)
raise ValueError ('value %s exceeds the integer width of the control (%d)' % (s, self._integerWidth))
elif s[0] not in ('-', ' ') and self._allowNegative and len(s) == (adjustwidth - self._groupSpace):
dbg(indent=0)
## dbg(indent=0)
raise ValueError ('value %s exceeds the integer width of the control (%d)' % (s, self._integerWidth))
s = s.rjust(adjustwidth).replace('.', self._decimalChar)
@ -1452,7 +1455,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
s = s.replace('-', '(') + ')'
else:
s += ' '
dbg('returned: "%s"' % s, indent=0)
## dbg('returned: "%s"' % s, indent=0)
return s
@ -1460,8 +1463,8 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
"""
Conversion function used in getting the value of the control.
"""
dbg(suspend=0)
dbg('MaskedNumCtrl::_fromGUI(%s)' % value, indent=1)
## dbg(suspend=0)
## dbg('NumCtrl::_fromGUI(%s)' % value, indent=1)
# One or more of the underlying text control implementations
# issue an intermediate EVT_TEXT when replacing the control's
# value, where the intermediate value is an empty string.
@ -1470,42 +1473,42 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
#
if value.strip() == '':
if not self.IsNoneAllowed():
dbg('empty value; not allowed,returning 0', indent = 0)
## dbg('empty value; not allowed,returning 0', indent = 0)
if self._fractionWidth:
return 0.0
else:
return 0
else:
dbg('empty value; returning None', indent = 0)
## dbg('empty value; returning None', indent = 0)
return None
else:
value = self._GetNumValue(value)
dbg('Num value: "%s"' % value)
## dbg('Num value: "%s"' % value)
if self._fractionWidth:
try:
dbg(indent=0)
## dbg(indent=0)
return float( value )
except ValueError:
dbg("couldn't convert to float; returning None")
## dbg("couldn't convert to float; returning None")
return None
else:
raise
else:
try:
dbg(indent=0)
## dbg(indent=0)
return int( value )
except ValueError:
try:
dbg(indent=0)
## dbg(indent=0)
return long( value )
except ValueError:
dbg("couldn't convert to long; returning None")
## dbg("couldn't convert to long; returning None")
return None
else:
raise
else:
dbg('exception occurred; returning None')
## dbg('exception occurred; returning None')
return None
@ -1514,7 +1517,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
Preprocessor for base control paste; if value needs to be right-justified
to fit in control, do so prior to paste:
"""
dbg('MaskedNumCtrl::_Paste (value = "%s")' % value)
## dbg('NumCtrl::_Paste (value = "%s")' % value)
if value is None:
paste_text = self._getClipboardContents()
else:
@ -1545,7 +1548,7 @@ if __name__ == '__main__':
style = wx.DEFAULT_DIALOG_STYLE ):
wx.Dialog.__init__(self, parent, id, title, pos, size, style)
self.int_ctrl = MaskedNumCtrl(self, wx.NewId(), size=(55,20))
self.int_ctrl = NumCtrl(self, wx.NewId(), size=(55,20))
self.OK = wx.Button( self, wx.ID_OK, "OK")
self.Cancel = wx.Button( self, wx.ID_CANCEL, "Cancel")
@ -1560,7 +1563,7 @@ if __name__ == '__main__':
self.SetSizer( vs )
vs.Fit( self )
vs.SetSizeHints( self )
self.Bind(EVT_MASKEDNUM, self.OnChange, self.int_ctrl)
self.Bind(EVT_NUM, self.OnChange, self.int_ctrl)
def OnChange(self, event):
print 'value now', event.GetValue()
@ -1578,7 +1581,7 @@ if __name__ == '__main__':
return True
def OnClick(self, event):
dlg = myDialog(self.panel, -1, "test MaskedNumCtrl")
dlg = myDialog(self.panel, -1, "test NumCtrl")
dlg.int_ctrl.SetValue(501)
dlg.int_ctrl.SetInsertionPoint(1)
dlg.int_ctrl.SetSelection(1,2)

View File

@ -0,0 +1,325 @@
#----------------------------------------------------------------------------
# Name: masked.textctrl.py
# Authors: Jeff Childers, Will Sadkin
# Email: jchilders_98@yahoo.com, wsadkin@nameconnector.com
# Created: 02/11/2003
# Copyright: (c) 2003 by Jeff Childers, Will Sadkin, 2003
# Portions: (c) 2002 by Will Sadkin, 2002-2003
# RCS-ID: $Id$
# License: wxWidgets license
#----------------------------------------------------------------------------
#
# This file contains the most typically used generic masked control,
# masked.TextCtrl. It also defines the BaseMaskedTextCtrl, which can
# be used to derive other "semantics-specific" classes, like masked.NumCtrl,
# masked.TimeCtrl, and masked.IpAddrCtrl.
#
#----------------------------------------------------------------------------
import wx
from wx.lib.masked import *
# jmg 12/9/03 - when we cut ties with Py 2.2 and earlier, this would
# be a good place to implement the 2.3 logger class
from wx.tools.dbg import Logger
dbg = Logger()
##dbg(enable=0)
# ## TRICKY BIT: to avoid a ton of boiler-plate, and to
# ## automate the getter/setter generation for each valid
# ## control parameter so we never forget to add the
# ## functions when adding parameters, this loop
# ## programmatically adds them to the class:
# ## (This makes it easier for Designers like Boa to
# ## deal with masked controls.)
#
# ## To further complicate matters, this is done with an
# ## extra level of inheritance, so that "general" classes like
# ## MaskedTextCtrl can have all possible attributes,
# ## while derived classes, like TimeCtrl and MaskedNumCtrl
# ## can prevent exposure of those optional attributes of their base
# ## class that do not make sense for their derivation. Therefore,
# ## we define
# ## BaseMaskedTextCtrl(TextCtrl, MaskedEditMixin)
# ## and
# ## MaskedTextCtrl(BaseMaskedTextCtrl, MaskedEditAccessorsMixin).
# ##
# ## This allows us to then derive:
# ## MaskedNumCtrl( BaseMaskedTextCtrl )
# ##
# ## and not have to expose all the same accessor functions for the
# ## derived control when they don't all make sense for it.
# ##
class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
"""
This is the primary derivation from MaskedEditMixin. It provides
a general masked text control that can be configured with different
masks. It's actually a "base masked textCtrl", so that the
MaskedTextCtrl class can be derived from it, and add those
accessor functions to it that are appropriate to the general class,
whilst other classes can derive from BaseMaskedTextCtrl, and
only define those accessor functions that are appropriate for
those derivations.
"""
def __init__( self, parent, id=-1, value = '',
pos = wx.DefaultPosition,
size = wx.DefaultSize,
style = wx.TE_PROCESS_TAB,
validator=wx.DefaultValidator, ## placeholder provided for data-transfer logic
name = 'maskedTextCtrl',
setupEventHandling = True, ## setup event handling by default
**kwargs):
wx.TextCtrl.__init__(self, parent, id, value='',
pos=pos, size = size,
style=style, validator=validator,
name=name)
self.controlInitialized = True
MaskedEditMixin.__init__( self, name, **kwargs )
self._SetInitialValue(value)
if setupEventHandling:
## Setup event handlers
self.Bind(wx.EVT_SET_FOCUS, self._OnFocus ) ## defeat automatic full selection
self.Bind(wx.EVT_KILL_FOCUS, self._OnKillFocus ) ## run internal validator
self.Bind(wx.EVT_LEFT_DCLICK, self._OnDoubleClick) ## select field under cursor on dclick
self.Bind(wx.EVT_RIGHT_UP, self._OnContextMenu ) ## bring up an appropriate context menu
self.Bind(wx.EVT_KEY_DOWN, self._OnKeyDown ) ## capture control events not normally seen, eg ctrl-tab.
self.Bind(wx.EVT_CHAR, self._OnChar ) ## handle each keypress
self.Bind(wx.EVT_TEXT, self._OnTextChange ) ## color control appropriately & keep
## track of previous value for undo
def __repr__(self):
return "<BaseMaskedTextCtrl: %s>" % self.GetValue()
def _GetSelection(self):
"""
Allow mixin to get the text selection of this control.
REQUIRED by any class derived from MaskedEditMixin.
"""
return self.GetSelection()
def _SetSelection(self, sel_start, sel_to):
"""
Allow mixin to set the text selection of this control.
REQUIRED by any class derived from MaskedEditMixin.
"""
#### dbg("MaskedTextCtrl::_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("MaskedTextCtrl::SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
wx.TextCtrl.SetSelection(self, sel_start, sel_to)
def _GetInsertionPoint(self):
return self.GetInsertionPoint()
def _SetInsertionPoint(self, pos):
#### dbg("MaskedTextCtrl::_SetInsertionPoint(%(pos)d)" % locals())
self.SetInsertionPoint(pos)
def SetInsertionPoint(self, pos):
"""
This is just for debugging...
"""
## dbg("MaskedTextCtrl::SetInsertionPoint(%(pos)d)" % locals())
wx.TextCtrl.SetInsertionPoint(self, pos)
def _GetValue(self):
"""
Allow mixin to get the raw value of the control with this function.
REQUIRED by any class derived from MaskedEditMixin.
"""
return self.GetValue()
def _SetValue(self, value):
"""
Allow mixin to set the raw value of the control with this function.
REQUIRED by any class derived from MaskedEditMixin.
"""
## dbg('MaskedTextCtrl::_SetValue("%(value)s")' % locals(), indent=1)
# Record current selection and insertion point, for undo
self._prevSelection = self._GetSelection()
self._prevInsertionPoint = self._GetInsertionPoint()
wx.TextCtrl.SetValue(self, value)
## dbg(indent=0)
def SetValue(self, value):
"""
This function redefines the externally accessible .SetValue to be
a smart "paste" of the text in question, so as not to corrupt the
masked control. NOTE: this must be done in the class derived
from the base wx control.
"""
## dbg('MaskedTextCtrl::SetValue = "%s"' % value, indent=1)
if not self._mask:
wx.TextCtrl.SetValue(self, value) # revert to base control behavior
return
# empty previous contents, replacing entire value:
self._SetInsertionPoint(0)
self._SetSelection(0, self._masklength)
if self._signOk and self._useParens:
signpos = value.find('-')
if signpos != -1:
value = value[:signpos] + '(' + value[signpos+1:].strip() + ')'
elif value.find(')') == -1 and len(value) < self._masklength:
value += ' ' # add place holder for reserved space for right paren
if( len(value) < self._masklength # value shorter than control
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)
# 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)
# make SetValue behave the same as if you had typed the value in:
try:
value = self._Paste(value, raise_on_invalid=True, just_return_value=True)
if self._isFloat:
self._isNeg = False # (clear current assumptions)
value = self._adjustFloat(value)
elif self._isInt:
self._isNeg = False # (clear current assumptions)
value = self._adjustInt(value)
elif self._isDate and not self.IsValid(value) and self._4digityear:
value = self._adjustDate(value, fixcentury=True)
except ValueError:
# If date, year might be 2 digits vs. 4; try adjusting it:
if self._isDate and self._4digityear:
dateparts = value.split(' ')
dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True)
value = string.join(dateparts, ' ')
## dbg('adjusted value: "%s"' % value)
value = self._Paste(value, raise_on_invalid=True, just_return_value=True)
else:
## dbg('exception thrown', indent=0)
raise
self._SetValue(value) # note: to preserve similar capability, .SetValue()
# does not change IsModified()
#### dbg('queuing insertion after .SetValue', self._masklength)
wx.CallAfter(self._SetInsertionPoint, self._masklength)
wx.CallAfter(self._SetSelection, self._masklength, self._masklength)
## dbg(indent=0)
def Clear(self):
""" Blanks the current control value by replacing it with the default value."""
## dbg("MaskedTextCtrl::Clear - value reset to default value (template)")
if self._mask:
self.ClearValue()
else:
wx.TextCtrl.Clear(self) # else revert to base control behavior
def _Refresh(self):
"""
Allow mixin to refresh the base control with this function.
REQUIRED by any class derived from MaskedEditMixin.
"""
## dbg('MaskedTextCtrl::_Refresh', indent=1)
wx.TextCtrl.Refresh(self)
## dbg(indent=0)
def Refresh(self):
"""
This function redefines the externally accessible .Refresh() to
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('MaskedTextCtrl::Refresh', indent=1)
self._CheckValid()
self._Refresh()
## dbg(indent=0)
def _IsEditable(self):
"""
Allow mixin to determine if the base control is editable with this function.
REQUIRED by any class derived from MaskedEditMixin.
"""
return wx.TextCtrl.IsEditable(self)
def Cut(self):
"""
This function redefines the externally accessible .Cut to be
a smart "erase" of the text in question, so as not to corrupt the
masked control. NOTE: this must be done in the class derived
from the base wx control.
"""
if self._mask:
self._Cut() # call the mixin's Cut method
else:
wx.TextCtrl.Cut(self) # else revert to base control behavior
def Paste(self):
"""
This function redefines the externally accessible .Paste to be
a smart "paste" of the text in question, so as not to corrupt the
masked control. NOTE: this must be done in the class derived
from the base wx control.
"""
if self._mask:
self._Paste() # call the mixin's Paste method
else:
wx.TextCtrl.Paste(self, value) # else revert to base control behavior
def Undo(self):
"""
This function defines the undo operation for the control. (The default
undo is 1-deep.)
"""
if self._mask:
self._Undo()
else:
wx.TextCtrl.Undo(self) # else revert to base control behavior
def IsModified(self):
"""
This function overrides the raw wxTextCtrl method, because the
masked edit mixin uses SetValue to change the value, which doesn't
modify the state of this attribute. So, we keep track on each
keystroke to see if the value changes, and if so, it's been
modified.
"""
return wx.TextCtrl.IsModified(self) or self.modified
def _CalcSize(self, size=None):
"""
Calculate automatic size if allowed; use base mixin function.
"""
return self._calcSize(size)
class TextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ):
"""
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

View File

@ -36,11 +36,11 @@
# o wx.SpinCtl has some issues that cause the control to
# lock up. Noted in other places using it too, it's not this module
# that's at fault.
#
#
# 12/20/2003 - Jeff Grimmett (grimmtooth@softhome.net)
#
# o wxMaskedTextCtrl -> MaskedTextCtrl
# o wxTimeCtrl -> TimeCtrl
# o wxMaskedTextCtrl -> masked.TextCtrl
# o wxTimeCtrl -> masked.TimeCtrl
#
"""<html><body>
@ -65,7 +65,7 @@ Here's the API for TimeCtrl:
<B>style</B> = wxTE_PROCESS_TAB,
<B>validator</B> = wx.DefaultValidator,
name = "time",
<B>format</B> = 'HHMMSS',
<B>format</B> = 'HHMMSS',
<B>fmt24hr</B> = False,
<B>displaySeconds</B> = True,
<B>spinButton</B> = None,
@ -95,7 +95,7 @@ Here's the API for TimeCtrl:
<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.
'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
@ -107,7 +107,7 @@ Here's the API for TimeCtrl:
<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>
<BR>
<DT><B>spinButton</B>
<DD>If specified, this button's events will be bound to the behavior of the
TimeCtrl, working like up/down cursor key events. (See BindSpinButton.)
@ -272,10 +272,10 @@ import types
import wx
from wx.tools.dbg import Logger
from wx.lib.maskededit import BaseMaskedTextCtrl, Field
from wx.lib.masked import Field, BaseMaskedTextCtrl
dbg = Logger()
dbg(enable=0)
##dbg(enable=0)
try:
from mx import DateTime
@ -322,8 +322,8 @@ class TimeCtrlAccessorsMixin:
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 = {
@ -333,7 +333,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
'max': None,
'limited': False, # by default, no limiting even if bounds set
'useFixedWidthFont': True, # by default, use a fixed-width font
'oob_color': "Yellow" # by default, the default MaskedTextCtrl "invalid" color
'oob_color': "Yellow" # by default, the default masked.TextCtrl "invalid" color
}
def __init__ (
@ -347,7 +347,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
**kwargs ):
# set defaults for control:
dbg('setting defaults:')
## dbg('setting defaults:')
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
@ -456,7 +456,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
def SetParameters(self, **kwargs):
dbg('TimeCtrl::SetParameters(%s)' % repr(kwargs), indent=1)
## dbg('TimeCtrl::SetParameters(%s)' % repr(kwargs), indent=1)
maskededit_kwargs = {}
reset_format = False
@ -553,10 +553,10 @@ class TimeCtrl(BaseMaskedTextCtrl):
self.SetValue(value)
except:
self.SetValue('12:00:00 AM')
dbg(indent=0)
## dbg(indent=0)
return {} # no arguments to return
else:
dbg(indent=0)
## dbg(indent=0)
return maskededit_kwargs
@ -565,7 +565,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
This function binds an externally created spin button to the control, so that
up/down events from the button automatically change the control.
"""
dbg('TimeCtrl::BindSpinButton')
## dbg('TimeCtrl::BindSpinButton')
self.__spinButton = sb
if self.__spinButton:
# bind event handlers to spin ctrl
@ -584,16 +584,16 @@ class TimeCtrl(BaseMaskedTextCtrl):
and convert wxDateTime, mxDateTime, or 12/24 format time string
into the appropriate format string for the control.
"""
dbg('TimeCtrl::SetValue(%s)' % repr(value), indent=1)
## dbg('TimeCtrl::SetValue(%s)' % repr(value), indent=1)
try:
strtime = self._toGUI(self.__validateValue(value))
except:
dbg('validation failed', indent=0)
## dbg('validation failed', indent=0)
raise
dbg('strtime:', strtime)
## dbg('strtime:', strtime)
self._SetValue(strtime)
dbg(indent=0)
## dbg(indent=0)
def GetValue(self,
as_wxDateTime = False,
@ -641,12 +641,12 @@ class TimeCtrl(BaseMaskedTextCtrl):
raised.
"""
global accept_mx
dbg(suspend=1)
dbg('TimeCtrl::GetWxDateTime(%s)' % repr(value), indent=1)
## dbg(suspend=1)
## dbg('TimeCtrl::GetWxDateTime(%s)' % repr(value), indent=1)
if value is None:
dbg('getting control value')
## dbg('getting control value')
value = self.GetValue()
dbg('value = "%s"' % value)
## dbg('value = "%s"' % value)
if type(value) == types.UnicodeType:
value = str(value) # convert to regular string
@ -656,14 +656,14 @@ class TimeCtrl(BaseMaskedTextCtrl):
# Construct constant wxDateTime, then try to parse the string:
wxdt = wx.DateTimeFromDMY(1, 0, 1970)
dbg('attempting conversion')
## dbg('attempting conversion')
value = value.strip() # (parser doesn't like leading spaces)
checkTime = wxdt.ParseTime(value)
valid = checkTime == len(value) # entire string parsed?
dbg('checkTime == len(value)?', valid)
## dbg('checkTime == len(value)?', valid)
if not valid:
dbg(indent=0, suspend=0)
## dbg(indent=0, suspend=0)
raise ValueError('cannot convert string "%s" to valid time' % value)
else:
@ -685,7 +685,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
error = 'GetWxDateTime requires wxDateTime, mxDateTime or parsable time string, passed %s'% repr(value)
else:
error = 'GetWxDateTime requires wxDateTime or parsable time string, passed %s'% repr(value)
dbg(indent=0, suspend=0)
## dbg(indent=0, suspend=0)
raise ValueError(error)
wxdt = wx.DateTimeFromDMY(1, 0, 1970)
@ -693,7 +693,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
wxdt.SetMinute(minute)
wxdt.SetSecond(second)
dbg('wxdt:', wxdt, indent=0, suspend=0)
## dbg('wxdt:', wxdt, indent=0, suspend=0)
return wxdt
@ -730,13 +730,13 @@ class TimeCtrl(BaseMaskedTextCtrl):
adjusted to the new minimum value; if not limited, the value in the
control will be colored as invalid.
"""
dbg('TimeCtrl::SetMin(%s)'% repr(min), indent=1)
## dbg('TimeCtrl::SetMin(%s)'% repr(min), indent=1)
if min is not None:
try:
min = self.GetWxDateTime(min)
self.__min = self._toGUI(min)
except:
dbg('exception occurred', indent=0)
## dbg('exception occurred', indent=0)
return False
else:
self.__min = min
@ -746,7 +746,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
else:
self._CheckValid()
ret = True
dbg('ret:', ret, indent=0)
## dbg('ret:', ret, indent=0)
return ret
@ -757,22 +757,23 @@ class TimeCtrl(BaseMaskedTextCtrl):
the current minimum bound on the control, as a wxDateTime
by default, or as a string if as_string argument is True.
"""
dbg(suspend=1)
dbg('TimeCtrl::GetMin, as_string?', as_string, indent=1)
## dbg(suspend=1)
## dbg('TimeCtrl::GetMin, as_string?', as_string, indent=1)
if self.__min is None:
dbg('(min == None)')
## dbg('(min == None)')
ret = self.__min
elif as_string:
ret = self.__min
dbg('ret:', ret)
## dbg('ret:', ret)
else:
try:
ret = self.GetWxDateTime(self.__min)
except:
dbg(suspend=0)
dbg('exception occurred', indent=0)
dbg('ret:', repr(ret))
dbg(indent=0, suspend=0)
## dbg(suspend=0)
## dbg('exception occurred', indent=0)
raise
## dbg('ret:', repr(ret))
## dbg(indent=0, suspend=0)
return ret
@ -789,23 +790,23 @@ class TimeCtrl(BaseMaskedTextCtrl):
adjusted to this maximum value; if not limited, the value in the
control will be colored as invalid.
"""
dbg('TimeCtrl::SetMax(%s)' % repr(max), indent=1)
## dbg('TimeCtrl::SetMax(%s)' % repr(max), indent=1)
if max is not None:
try:
max = self.GetWxDateTime(max)
self.__max = self._toGUI(max)
except:
dbg('exception occurred', indent=0)
## dbg('exception occurred', indent=0)
return False
else:
self.__max = max
dbg('max:', repr(self.__max))
## dbg('max:', repr(self.__max))
if self.IsLimited() and not self.IsInBounds():
self.SetLimited(self.__limited) # force limited value:
else:
self._CheckValid()
ret = True
dbg('ret:', ret, indent=0)
## dbg('ret:', ret, indent=0)
return ret
@ -816,23 +817,23 @@ class TimeCtrl(BaseMaskedTextCtrl):
the current minimum bound on the control, as a wxDateTime
by default, or as a string if as_string argument is True.
"""
dbg(suspend=1)
dbg('TimeCtrl::GetMin, as_string?', as_string, indent=1)
## dbg(suspend=1)
## dbg('TimeCtrl::GetMin, as_string?', as_string, indent=1)
if self.__max is None:
dbg('(max == None)')
## dbg('(max == None)')
ret = self.__max
elif as_string:
ret = self.__max
dbg('ret:', ret)
## dbg('ret:', ret)
else:
try:
ret = self.GetWxDateTime(self.__max)
except:
dbg(suspend=0)
dbg('exception occurred', indent=0)
## dbg(suspend=0)
## dbg('exception occurred', indent=0)
raise
dbg('ret:', repr(ret))
dbg(indent=0, suspend=0)
## dbg('ret:', repr(ret))
## dbg(indent=0, suspend=0)
return ret
@ -868,22 +869,22 @@ class TimeCtrl(BaseMaskedTextCtrl):
limiting, but coloring of out-of-bounds values will still take
place if bounds have been set for the control.
"""
dbg('TimeCtrl::SetLimited(%d)' % limited, indent=1)
## dbg('TimeCtrl::SetLimited(%d)' % limited, indent=1)
self.__limited = limited
if not limited:
self.SetMaskParameters(validRequired = False)
self._CheckValid()
dbg(indent=0)
## dbg(indent=0)
return
dbg('requiring valid value')
## dbg('requiring valid value')
self.SetMaskParameters(validRequired = True)
min = self.GetMin()
max = self.GetMax()
if min is None or max is None:
dbg('both bounds not set; no further action taken')
## dbg('both bounds not set; no further action taken')
return # can't limit without 2 bounds
elif not self.IsInBounds():
@ -891,11 +892,11 @@ class TimeCtrl(BaseMaskedTextCtrl):
try:
value = self.GetWxDateTime()
except:
dbg('exception occurred', indent=0)
## dbg('exception occurred', indent=0)
raise
if min <= max: # valid range doesn't span midnight
dbg('min <= max')
## dbg('min <= max')
# which makes the "nearest bound" computation trickier...
# determine how long the "invalid" pie wedge is, and cut
@ -918,24 +919,24 @@ class TimeCtrl(BaseMaskedTextCtrl):
cmp_value = value
if (cmp_value - max) > half_interval:
dbg('forcing value to min (%s)' % min.FormatTime())
## dbg('forcing value to min (%s)' % min.FormatTime())
self.SetValue(min)
else:
dbg('forcing value to max (%s)' % max.FormatTime())
## dbg('forcing value to max (%s)' % max.FormatTime())
self.SetValue(max)
else:
dbg('max < min')
## dbg('max < min')
# therefore max < value < min guaranteed to be true,
# so "nearest bound" calculation is much easier:
if (value - max) >= (min - value):
# current value closer to min; pick that edge of pie wedge
dbg('forcing value to min (%s)' % min.FormatTime())
## dbg('forcing value to min (%s)' % min.FormatTime())
self.SetValue(min)
else:
dbg('forcing value to max (%s)' % max.FormatTime())
## dbg('forcing value to max (%s)' % max.FormatTime())
self.SetValue(max)
dbg(indent=0)
## dbg(indent=0)
@ -961,21 +962,22 @@ class TimeCtrl(BaseMaskedTextCtrl):
try:
value = self.GetWxDateTime(value) # try to regularize passed value
except ValueError:
dbg('ValueError getting wxDateTime for %s' % repr(value), indent=0)
## dbg('ValueError getting wxDateTime for %s' % repr(value), indent=0)
raise
dbg('TimeCtrl::IsInBounds(%s)' % repr(value), indent=1)
## dbg('TimeCtrl::IsInBounds(%s)' % repr(value), indent=1)
if self.__min is None or self.__max is None:
dbg(indent=0)
## dbg(indent=0)
return True
elif value is None:
try:
value = self.GetWxDateTime()
except:
dbg('exception occurred', indent=0)
## dbg('exception occurred', indent=0)
raise
dbg('value:', value.FormatTime())
## dbg('value:', value.FormatTime())
# Get wxDateTime representations of bounds:
min = self.GetMin()
@ -990,7 +992,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
# either "min" <= value (<= midnight of *next day*)
# or midnight <= value <= "max"
ret = min <= value or (midnight <= value <= max)
dbg('in bounds?', ret, indent=0)
## dbg('in bounds?', ret, indent=0)
return ret
@ -1021,7 +1023,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
def __OnTextChange(self, event=None):
dbg('TimeCtrl::OnTextChange', indent=1)
## dbg('TimeCtrl::OnTextChange', indent=1)
# Allow Maskedtext base control to color as appropriate,
# and Skip the EVT_TEXT event (if appropriate.)
@ -1035,11 +1037,11 @@ class TimeCtrl(BaseMaskedTextCtrl):
if not BaseMaskedTextCtrl._OnTextChange(self, event):
return
dbg('firing TimeUpdatedEvent...')
## dbg('firing TimeUpdatedEvent...')
evt = TimeUpdatedEvent(self.GetId(), self.GetValue())
evt.SetEventObject(self)
self.GetEventHandler().ProcessEvent(evt)
dbg(indent=0)
## dbg(indent=0)
def SetInsertionPoint(self, pos):
@ -1048,14 +1050,14 @@ class TimeCtrl(BaseMaskedTextCtrl):
This is necessary to handle the optional spin button, because the insertion
point is lost when the focus shifts to the spin button.
"""
dbg('TimeCtrl::SetInsertionPoint', pos, indent=1)
## dbg('TimeCtrl::SetInsertionPoint', pos, indent=1)
BaseMaskedTextCtrl.SetInsertionPoint(self, pos) # (causes EVT_TEXT event to fire)
self.__posCurrent = self.GetInsertionPoint()
dbg(indent=0)
## dbg(indent=0)
def SetSelection(self, sel_start, sel_to):
dbg('TimeCtrl::SetSelection', sel_start, sel_to, indent=1)
## dbg('TimeCtrl::SetSelection', sel_start, sel_to, indent=1)
# Adjust selection range to legal extent if not already
if sel_start < 0:
@ -1069,7 +1071,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
self.__bSelection = sel_start != sel_to
BaseMaskedTextCtrl.SetSelection(self, sel_start, sel_to)
dbg(indent=0)
## dbg(indent=0)
def __OnSpin(self, key):
@ -1085,7 +1087,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
start, end = self._FindField(self.__posCurrent)._extent
self.SetInsertionPoint(start)
self.SetSelection(start, end)
dbg('current position:', self.__posCurrent)
## dbg('current position:', self.__posCurrent)
def __OnSpinUp(self, event):
@ -1093,10 +1095,10 @@ class TimeCtrl(BaseMaskedTextCtrl):
Event handler for any bound spin button on EVT_SPIN_UP;
causes control to behave as if up arrow was pressed.
"""
dbg('TimeCtrl::OnSpinUp', indent=1)
## dbg('TimeCtrl::OnSpinUp', indent=1)
self.__OnSpin(wx.WXK_UP)
keep_processing = False
dbg(indent=0)
## dbg(indent=0)
return keep_processing
@ -1105,10 +1107,10 @@ class TimeCtrl(BaseMaskedTextCtrl):
Event handler for any bound spin button on EVT_SPIN_DOWN;
causes control to behave as if down arrow was pressed.
"""
dbg('TimeCtrl::OnSpinDown', indent=1)
## dbg('TimeCtrl::OnSpinDown', indent=1)
self.__OnSpin(wx.WXK_DOWN)
keep_processing = False
dbg(indent=0)
## dbg(indent=0)
return keep_processing
@ -1119,14 +1121,14 @@ class TimeCtrl(BaseMaskedTextCtrl):
It then calls the base control's _OnChar routine with the modified
event instance.
"""
dbg('TimeCtrl::OnChar', indent=1)
## dbg('TimeCtrl::OnChar', indent=1)
keycode = event.GetKeyCode()
dbg('keycode:', keycode)
## dbg('keycode:', keycode)
if keycode == ord(':'):
dbg('colon seen! removing shift attribute')
## dbg('colon seen! removing shift attribute')
event.m_shiftDown = False
BaseMaskedTextCtrl._OnChar(self, event ) ## handle each keypress
dbg(indent=0)
## dbg(indent=0)
def __OnSetToNow(self, event):
@ -1144,7 +1146,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
Event handler for motion events; this handler
changes limits the selection to the new cell boundaries.
"""
dbg('TimeCtrl::LimitSelection', indent=1)
## dbg('TimeCtrl::LimitSelection', indent=1)
pos = self.GetInsertionPoint()
self.__posCurrent = pos
sel_start, sel_to = self.GetSelection()
@ -1155,18 +1157,18 @@ class TimeCtrl(BaseMaskedTextCtrl):
if sel_to < pos: sel_to = start
elif sel_to > pos: sel_to = end
dbg('new pos =', self.__posCurrent, 'select to ', sel_to)
## dbg('new pos =', self.__posCurrent, 'select to ', sel_to)
self.SetInsertionPoint(self.__posCurrent)
self.SetSelection(self.__posCurrent, sel_to)
if event: event.Skip()
dbg(indent=0)
## dbg(indent=0)
def __IncrementValue(self, key, pos):
dbg('TimeCtrl::IncrementValue', key, pos, indent=1)
## dbg('TimeCtrl::IncrementValue', key, pos, indent=1)
text = self.GetValue()
field = self._FindField(pos)
dbg('field: ', field._index)
## dbg('field: ', field._index)
start, end = field._extent
slice = text[start:end]
if key == wx.WXK_UP: increment = 1
@ -1182,14 +1184,14 @@ class TimeCtrl(BaseMaskedTextCtrl):
# am/pm setting. So, we use wxDateTime to generate a new value for us:
# (Use a fixed date not subject to DST variations:)
converter = wx.DateTimeFromDMY(1, 0, 1970)
dbg('text: "%s"' % text)
## dbg('text: "%s"' % text)
converter.ParseTime(text.strip())
currenthour = converter.GetHour()
dbg('current hour:', currenthour)
## dbg('current hour:', currenthour)
newhour = (currenthour + increment) % 24
dbg('newhour:', newhour)
## dbg('newhour:', newhour)
converter.SetHour(newhour)
dbg('converter.GetHour():', converter.GetHour())
## dbg('converter.GetHour():', converter.GetHour())
newvalue = converter # take advantage of auto-conversion for am/pm in .SetValue()
else: # minute or second field; handled the same way:
@ -1202,7 +1204,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
except ValueError: # must not be in bounds:
if not wx.Validator_IsSilent():
wx.Bell()
dbg(indent=0)
## dbg(indent=0)
def _toGUI( self, wxdt ):
@ -1227,23 +1229,23 @@ class TimeCtrl(BaseMaskedTextCtrl):
not a valid value for the control as currently specified.
It is used by both the SetValue() and the IsValid() methods.
"""
dbg('TimeCtrl::__validateValue(%s)' % repr(value), indent=1)
## dbg('TimeCtrl::__validateValue(%s)' % repr(value), indent=1)
if not value:
dbg(indent=0)
## dbg(indent=0)
raise ValueError('%s not a valid time value' % repr(value))
valid = True # assume true
try:
value = self.GetWxDateTime(value) # regularize form; can generate ValueError if problem doing so
except:
dbg('exception occurred', indent=0)
## dbg('exception occurred', indent=0)
raise
if self.IsLimited() and not self.IsInBounds(value):
dbg(indent=0)
## dbg(indent=0)
raise ValueError (
'value %s is not within the bounds of the control' % str(value) )
dbg(indent=0)
## dbg(indent=0)
return value
#----------------------------------------------------------------------------
@ -1278,12 +1280,12 @@ if __name__ == '__main__':
self.Bind(EVT_TIMEUPDATE, self.OnTimeChange, self.tc)
def OnTimeChange(self, event):
dbg('OnTimeChange: value = ', event.GetValue())
## dbg('OnTimeChange: value = ', event.GetValue())
wxdt = self.tc.GetWxDateTime()
dbg('wxdt =', wxdt.GetHour(), wxdt.GetMinute(), wxdt.GetSecond())
## dbg('wxdt =', wxdt.GetHour(), wxdt.GetMinute(), wxdt.GetSecond())
if self.test_mx:
mxdt = self.tc.GetMxDateTime()
dbg('mxdt =', mxdt.hour, mxdt.minute, mxdt.second)
## dbg('mxdt =', mxdt.hour, mxdt.minute, mxdt.second)
class MyApp(wx.App):

View File

@ -2,9 +2,21 @@
## backwards compatibility. Some names will also have a 'wx' added on if
## that is how they used to be named in the old wxPython package.
import wx.lib.maskedctrl
import wx.lib.masked.ctrl
__doc__ = wx.lib.maskedctrl.__doc__
__doc__ = wx.lib.masked.ctrl.__doc__
controlTypes = wx.lib.maskedctrl.controlTypes
wxMaskedCtrl = wx.lib.maskedctrl.MaskedCtrl
MASKEDTEXT = wx.lib.masked.ctrl.TEXT
MASKEDCOMBO = wx.lib.masked.ctrl.COMBO
IPADDR = wx.lib.masked.ctrl.IPADDR
TIME = wx.lib.masked.ctrl.TIME
NUMBER = wx.lib.masked.ctrl.NUMBER
class controlTypes:
MASKEDTEXT = wx.lib.masked.ctrl.TEXT
MASKEDCOMBO = wx.lib.masked.ctrl.COMBO
IPADDR = wx.lib.masked.ctrl.IPADDR
TIME = wx.lib.masked.ctrl.TIME
NUMBER = wx.lib.masked.ctrl.NUMBER
wxMaskedCtrl = wx.lib.masked.Ctrl

View File

@ -1,16 +1,15 @@
## This file imports items from the wx package into the wxPython package for
## backwards compatibility. Some names will also have a 'wx' added on if
## that is how they used to be named in the old wxPython package.
import wx.lib.masked
import wx.lib.masked.maskededit
import wx.lib.maskededit
__doc__ = wx.lib.masked.maskededit.__doc__
__doc__ = wx.lib.maskededit.__doc__
from wx.lib.masked.maskededit import *
Field = wx.lib.maskededit.Field
test = wx.lib.maskededit.test
test2 = wx.lib.maskededit.test2
wxIpAddrCtrl = wx.lib.maskededit.IpAddrCtrl
wxMaskedComboBox = wx.lib.maskededit.MaskedComboBox
wxMaskedComboBoxSelectEvent = wx.lib.maskededit.MaskedComboBoxSelectEvent
wxMaskedEditMixin = wx.lib.maskededit.MaskedEditMixin
wxMaskedTextCtrl = wx.lib.maskededit.MaskedTextCtrl
wxMaskedEditMixin = wx.lib.masked.MaskedEditMixin
wxMaskedTextCtrl = wx.lib.masked.TextCtrl
wxMaskedComboBox = wx.lib.masked.ComboBox
wxMaskedComboBoxSelectEvent = wx.lib.masked.MaskedComboBoxSelectEvent
wxIpAddrCtrl = wx.lib.masked.IpAddrCtrl

View File

@ -2,10 +2,10 @@
## backwards compatibility. Some names will also have a 'wx' added on if
## that is how they used to be named in the old wxPython package.
import wx.lib.maskednumctrl
import wx.lib.masked.numctrl
__doc__ = wx.lib.maskednumctrl.__doc__
__doc__ = wx.lib.masked.numctrl.__doc__
EVT_MASKEDNUM = wx.lib.maskednumctrl.EVT_MASKEDNUM
wxMaskedNumCtrl = wx.lib.maskednumctrl.MaskedNumCtrl
wxMaskedNumNumberUpdatedEvent = wx.lib.maskednumctrl.MaskedNumNumberUpdatedEvent
EVT_MASKEDNUM = wx.lib.masked.numctrl.EVT_NUM
wxMaskedNumCtrl = wx.lib.masked.numctrl.NumCtrl
wxMaskedNumNumberUpdatedEvent = wx.lib.masked.numctrl.NumberUpdatedEvent

View File

@ -2,10 +2,10 @@
## backwards compatibility. Some names will also have a 'wx' added on if
## that is how they used to be named in the old wxPython package.
import wx.lib.timectrl
import wx.lib.masked.timectrl
__doc__ = wx.lib.timectrl.__doc__
__doc__ = wx.lib.masked.timectrl.__doc__
EVT_TIMEUPDATE = wx.lib.timectrl.EVT_TIMEUPDATE
TimeUpdatedEvent = wx.lib.timectrl.TimeUpdatedEvent
wxTimeCtrl = wx.lib.timectrl.TimeCtrl
EVT_TIMEUPDATE = wx.lib.masked.timectrl.EVT_TIMEUPDATE
TimeUpdatedEvent = wx.lib.masked.timectrl.TimeUpdatedEvent
wxTimeCtrl = wx.lib.masked.timectrl.TimeCtrl