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:
parent
6cffbf02c0
commit
c878ceeae8
@ -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>
|
||||
"""
|
||||
|
||||
|
@ -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__':
|
||||
|
@ -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])])
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
20
wxPython/wx/lib/masked/__init__.py
Normal file
20
wxPython/wx/lib/masked/__init__.py
Normal 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
|
540
wxPython/wx/lib/masked/combobox.py
Normal file
540
wxPython/wx/lib/masked/combobox.py
Normal 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
|
||||
|
||||
|
@ -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(
|
187
wxPython/wx/lib/masked/ipaddrctrl.py
Normal file
187
wxPython/wx/lib/masked/ipaddrctrl.py
Normal 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)
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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)
|
325
wxPython/wx/lib/masked/textctrl.py
Normal file
325
wxPython/wx/lib/masked/textctrl.py
Normal 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
|
||||
|
||||
|
@ -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):
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user