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,8 +4,7 @@ import sys
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import wx
|
import wx
|
||||||
import wx.lib.maskededit as med
|
import wx.lib.masked as masked
|
||||||
import wx.lib.maskedctrl as mctl
|
|
||||||
import wx.lib.scrolledpanel as scroll
|
import wx.lib.scrolledpanel as scroll
|
||||||
|
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ class demoMixin:
|
|||||||
sizer.Add( wx.StaticText( self, -1, control[4]) )
|
sizer.Add( wx.StaticText( self, -1, control[4]) )
|
||||||
|
|
||||||
if control in controls:
|
if control in controls:
|
||||||
newControl = med.MaskedTextCtrl( self, -1, "",
|
newControl = masked.TextCtrl( self, -1, "",
|
||||||
mask = control[1],
|
mask = control[1],
|
||||||
excludeChars = control[2],
|
excludeChars = control[2],
|
||||||
formatcodes = control[3],
|
formatcodes = control[3],
|
||||||
@ -79,7 +78,7 @@ class demoPage1(scroll.ScrolledPanel, demoMixin):
|
|||||||
self.editList = []
|
self.editList = []
|
||||||
|
|
||||||
label = wx.StaticText( self, -1, """\
|
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
|
with this control. Note that all controls have been auto-sized by including 'F' in
|
||||||
the format codes.
|
the format codes.
|
||||||
|
|
||||||
@ -152,8 +151,8 @@ class demoPage2(scroll.ScrolledPanel, demoMixin):
|
|||||||
|
|
||||||
label = wx.StaticText( self, -1, """\
|
label = wx.StaticText( self, -1, """\
|
||||||
All these controls have been created by passing a single parameter, the autoformat code,
|
All these controls have been created by passing a single parameter, the autoformat code,
|
||||||
and use the factory class MaskedCtrl with its default controlType.
|
and use the factory class masked.Ctrl with its default controlType.
|
||||||
The maskededit module contains an internal dictionary of types and formats (autoformats).
|
The masked package contains an internal dictionary of types and formats (autoformats).
|
||||||
Many of these already do complicated validation; To see some examples, try
|
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.
|
29 Feb 2002 vs. 2004 for the date formats, or email address validation.
|
||||||
""")
|
""")
|
||||||
@ -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( autofmt, 0, wx.ALIGN_LEFT )
|
||||||
grid.Add( ctrl, 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, desc), 0, wx.ALIGN_LEFT )
|
||||||
grid.Add( wx.StaticText( self, -1, autoformat), 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,
|
autoformat = autoformat,
|
||||||
demo = True,
|
demo = True,
|
||||||
name = autoformat),
|
name = autoformat),
|
||||||
@ -197,7 +196,7 @@ class demoPage3(scroll.ScrolledPanel, demoMixin):
|
|||||||
self.editList = []
|
self.editList = []
|
||||||
|
|
||||||
label = wx.StaticText( self, -1, """\
|
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
|
control has a list of valid values, and the unsigned integer
|
||||||
has a legal range specified.
|
has a legal range specified.
|
||||||
""")
|
""")
|
||||||
@ -215,7 +214,7 @@ has a legal range specified.
|
|||||||
|
|
||||||
controls = [
|
controls = [
|
||||||
#description mask excl format regexp range,list,initial
|
#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 (signed)", "#{6}", "", 'F-_', "", '','', ' 0 '),
|
||||||
("Integer (unsigned)\n(1-399)","######", "", 'F_', "", (1,399),'', '1 '),
|
("Integer (unsigned)\n(1-399)","######", "", 'F_', "", (1,399),'', '1 '),
|
||||||
("Float (signed)", "#{6}.#{9}", "", 'F-_R', "", '','', '000000.000000000'),
|
("Float (signed)", "#{6}.#{9}", "", 'F-_R', "", '','', '000000.000000000'),
|
||||||
@ -270,7 +269,7 @@ Page Up and Shift-Up arrow will similarly cycle backwards through the list.
|
|||||||
grid.Add( ctrl, 0, wx.ALIGN_LEFT )
|
grid.Add( ctrl, 0, wx.ALIGN_LEFT )
|
||||||
|
|
||||||
autoformat = "USPHONEFULLEXT"
|
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 = """\
|
fieldsLabel = """\
|
||||||
{0: Field(choices=[
|
{0: Field(choices=[
|
||||||
"617","781",
|
"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, "Restricted Area Code"), 0, wx.ALIGN_LEFT )
|
||||||
grid.Add( wx.StaticText( self, -1, autoformat), 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( wx.StaticText( self, -1, fieldsLabel), 0, wx.ALIGN_LEFT )
|
||||||
grid.Add( med.MaskedTextCtrl( self, -1, "",
|
grid.Add( masked.TextCtrl( self, -1, "",
|
||||||
autoformat = autoformat,
|
autoformat = autoformat,
|
||||||
fields = fieldsDict,
|
fields = fieldsDict,
|
||||||
demo = True,
|
demo = True,
|
||||||
@ -287,12 +286,12 @@ Page Up and Shift-Up arrow will similarly cycle backwards through the list.
|
|||||||
0, wx.ALIGN_LEFT )
|
0, wx.ALIGN_LEFT )
|
||||||
|
|
||||||
autoformat = "EXPDATEMMYY"
|
autoformat = "EXPDATEMMYY"
|
||||||
fieldsDict = {1: med.Field(choices=["03", "04", "05"], choiceRequired=True)}
|
fieldsDict = {1: masked.Field(choices=["03", "04", "05"], choiceRequired=True)}
|
||||||
fieldsLabel = """\
|
fieldsLabel = """\
|
||||||
{1: Field(choices=[
|
{1: Field(choices=[
|
||||||
"03", "04", "05"],
|
"03", "04", "05"],
|
||||||
choiceRequired=True)}"""
|
choiceRequired=True)}"""
|
||||||
exp = med.MaskedTextCtrl( self, -1, "",
|
exp = masked.TextCtrl( self, -1, "",
|
||||||
autoformat = autoformat,
|
autoformat = autoformat,
|
||||||
fields = fieldsDict,
|
fields = fieldsDict,
|
||||||
demo = True,
|
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( wx.StaticText( self, -1, fieldsLabel), 0, wx.ALIGN_LEFT )
|
||||||
grid.Add( exp, 0, wx.ALIGN_LEFT )
|
grid.Add( exp, 0, wx.ALIGN_LEFT )
|
||||||
|
|
||||||
fieldsDict = {0: med.Field(choices=["02134","02155"], choiceRequired=True),
|
fieldsDict = {0: masked.Field(choices=["02134","02155"], choiceRequired=True),
|
||||||
1: med.Field(choices=["1234", "5678"], choiceRequired=False)}
|
1: masked.Field(choices=["1234", "5678"], choiceRequired=False)}
|
||||||
fieldsLabel = """\
|
fieldsLabel = """\
|
||||||
{0: Field(choices=["02134","02155"],
|
{0: Field(choices=["02134","02155"],
|
||||||
choiceRequired=True),
|
choiceRequired=True),
|
||||||
1: Field(choices=["1234", "5678"],
|
1: Field(choices=["1234", "5678"],
|
||||||
choiceRequired=False)}"""
|
choiceRequired=False)}"""
|
||||||
autoformat = "USZIPPLUS4"
|
autoformat = "USZIPPLUS4"
|
||||||
zip = med.MaskedTextCtrl( self, -1, "",
|
zip = masked.TextCtrl( self, -1, "",
|
||||||
autoformat = autoformat,
|
autoformat = autoformat,
|
||||||
fields = fieldsDict,
|
fields = fieldsDict,
|
||||||
demo = True,
|
demo = True,
|
||||||
@ -336,7 +335,7 @@ class demoPage5(scroll.ScrolledPanel, demoMixin):
|
|||||||
|
|
||||||
|
|
||||||
labelMaskedCombos = wx.StaticText( self, -1, """\
|
labelMaskedCombos = wx.StaticText( self, -1, """\
|
||||||
These are some examples of MaskedComboBox:""")
|
These are some examples of masked.ComboBox:""")
|
||||||
labelMaskedCombos.SetForegroundColour( "Blue" )
|
labelMaskedCombos.SetForegroundColour( "Blue" )
|
||||||
|
|
||||||
|
|
||||||
@ -344,8 +343,8 @@ These are some examples of MaskedComboBox:""")
|
|||||||
A state selector; only
|
A state selector; only
|
||||||
"legal" values can be
|
"legal" values can be
|
||||||
entered:""")
|
entered:""")
|
||||||
statecode = med.MaskedComboBox( self, -1, med.states[0],
|
statecode = masked.ComboBox( self, -1, masked.states[0],
|
||||||
choices = med.states,
|
choices = masked.states,
|
||||||
autoformat="USSTATE")
|
autoformat="USSTATE")
|
||||||
|
|
||||||
label_statename = wx.StaticText( self, -1, """\
|
label_statename = wx.StaticText( self, -1, """\
|
||||||
@ -353,9 +352,9 @@ A state name selector,
|
|||||||
with auto-select:""")
|
with auto-select:""")
|
||||||
|
|
||||||
# Create this one using factory function:
|
# Create this one using factory function:
|
||||||
statename = mctl.MaskedCtrl( self, -1, med.state_names[0],
|
statename = masked.Ctrl( self, -1, masked.state_names[0],
|
||||||
controlType = mctl.controlTypes.MASKEDCOMBO,
|
controlType = masked.controlTypes.COMBO,
|
||||||
choices = med.state_names,
|
choices = masked.state_names,
|
||||||
autoformat="USSTATENAME",
|
autoformat="USSTATENAME",
|
||||||
autoSelect=True)
|
autoSelect=True)
|
||||||
statename.SetCtrlParameters(formatcodes = 'F!V_')
|
statename.SetCtrlParameters(formatcodes = 'F!V_')
|
||||||
@ -363,8 +362,8 @@ with auto-select:""")
|
|||||||
|
|
||||||
numerators = [ str(i) for i in range(1, 4) ]
|
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] ]
|
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),
|
fieldsDict = {0: masked.Field(choices=numerators, choiceRequired=False),
|
||||||
1: med.Field(choices=denominators, choiceRequired=True)}
|
1: masked.Field(choices=denominators, choiceRequired=True)}
|
||||||
choices = []
|
choices = []
|
||||||
for n in numerators:
|
for n in numerators:
|
||||||
for d in denominators:
|
for d in denominators:
|
||||||
@ -377,8 +376,8 @@ A masked ComboBox for fraction selection.
|
|||||||
Choices for each side of the fraction can
|
Choices for each side of the fraction can
|
||||||
be selected with PageUp/Down:""")
|
be selected with PageUp/Down:""")
|
||||||
|
|
||||||
fraction = mctl.MaskedCtrl( self, -1, "",
|
fraction = masked.Ctrl( self, -1, "",
|
||||||
controlType = mctl.MASKEDCOMBO,
|
controlType = masked.controlTypes.COMBO,
|
||||||
choices = choices,
|
choices = choices,
|
||||||
choiceRequired = True,
|
choiceRequired = True,
|
||||||
mask = "#/##",
|
mask = "#/##",
|
||||||
@ -392,7 +391,7 @@ A masked ComboBox to validate
|
|||||||
text from a list of numeric codes:""")
|
text from a list of numeric codes:""")
|
||||||
|
|
||||||
choices = ["91", "136", "305", "4579"]
|
choices = ["91", "136", "305", "4579"]
|
||||||
code = med.MaskedComboBox( self, -1, choices[0],
|
code = masked.ComboBox( self, -1, choices[0],
|
||||||
choices = choices,
|
choices = choices,
|
||||||
choiceRequired = True,
|
choiceRequired = True,
|
||||||
formatcodes = "F_r",
|
formatcodes = "F_r",
|
||||||
@ -402,8 +401,8 @@ text from a list of numeric codes:""")
|
|||||||
Programmatically set
|
Programmatically set
|
||||||
choice sets:""")
|
choice sets:""")
|
||||||
self.list_selector = wx.ComboBox(self, -1, '', choices = ['list1', 'list2', 'list3'])
|
self.list_selector = wx.ComboBox(self, -1, '', choices = ['list1', 'list2', 'list3'])
|
||||||
self.dynamicbox = mctl.MaskedCtrl( self, -1, ' ',
|
self.dynamicbox = masked.Ctrl( self, -1, ' ',
|
||||||
controlType = mctl.controlTypes.MASKEDCOMBO,
|
controlType = masked.controlTypes.COMBO,
|
||||||
mask = 'XXXX',
|
mask = 'XXXX',
|
||||||
formatcodes = 'F_',
|
formatcodes = 'F_',
|
||||||
# these are to give dropdown some initial height,
|
# these are to give dropdown some initial height,
|
||||||
@ -415,23 +414,23 @@ choice sets:""")
|
|||||||
|
|
||||||
|
|
||||||
labelIpAddrs = wx.StaticText( self, -1, """\
|
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" )
|
labelIpAddrs.SetForegroundColour( "Blue" )
|
||||||
|
|
||||||
|
|
||||||
label_ipaddr1 = wx.StaticText( self, -1, "An empty control:")
|
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:")
|
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, """\
|
label_ipaddr3 = wx.StaticText( self, -1, """\
|
||||||
A control with restricted legal values:
|
A control with restricted legal values:
|
||||||
10. (1|2) . (129..255) . (0..255)""")
|
10. (1|2) . (129..255) . (0..255)""")
|
||||||
ipaddr3 = mctl.MaskedCtrl( self, -1,
|
ipaddr3 = masked.Ctrl( self, -1,
|
||||||
controlType = mctl.controlTypes.IPADDR,
|
controlType = masked.controlTypes.IPADDR,
|
||||||
mask=" 10. #.###.###")
|
mask=" 10. #.###.###")
|
||||||
ipaddr3.SetFieldParameters(0, validRegex="1|2",validRequired=False ) # requires entry to match or not allowed
|
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, """\
|
labelNumerics = wx.StaticText( self, -1, """\
|
||||||
Here are some useful configurations of a MaskedTextCtrl for integer and floating point input that still treat
|
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 MaskedNumCtrl class!)""")
|
the control as a text control. (For a true numeric control, check out the masked.NumCtrl class!)""")
|
||||||
labelNumerics.SetForegroundColour( "Blue" )
|
labelNumerics.SetForegroundColour( "Blue" )
|
||||||
|
|
||||||
label_intctrl1 = wx.StaticText( self, -1, """\
|
label_intctrl1 = wx.StaticText( self, -1, """\
|
||||||
An integer entry control with
|
An integer entry control with
|
||||||
shifting insert enabled:""")
|
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, """\
|
label_intctrl2 = wx.StaticText( self, -1, """\
|
||||||
Right-insert integer entry:""")
|
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, """\
|
label_floatctrl = wx.StaticText( self, -1, """\
|
||||||
A floating point entry control
|
A floating point entry control
|
||||||
with right-insert for ordinal:""")
|
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(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
|
self.floatctrl.SetFieldParameters(1, defaultValue='00') # don't allow blank fraction
|
||||||
|
|
||||||
@ -588,7 +587,7 @@ with right-insert for ordinal:""")
|
|||||||
formatcodes += 'r'
|
formatcodes += 'r'
|
||||||
mask = '###'
|
mask = '###'
|
||||||
else:
|
else:
|
||||||
choices = med.states
|
choices = masked.states
|
||||||
mask = 'AA'
|
mask = 'AA'
|
||||||
formatcodes += '!'
|
formatcodes += '!'
|
||||||
self.dynamicbox.SetCtrlParameters( mask = mask,
|
self.dynamicbox.SetCtrlParameters( mask = mask,
|
||||||
@ -628,15 +627,15 @@ def runTest(frame, nb, log):
|
|||||||
|
|
||||||
def RunStandalone():
|
def RunStandalone():
|
||||||
app = wx.PySimpleApp()
|
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)
|
win = TestMaskedTextCtrls(frame, -1, sys.stdout)
|
||||||
frame.Show(True)
|
frame.Show(True)
|
||||||
app.MainLoop()
|
app.MainLoop()
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
|
import wx.lib.masked.maskededit as maskededit
|
||||||
overview = """<html>
|
overview = """<html>
|
||||||
<PRE><FONT SIZE=-1>
|
<PRE><FONT SIZE=-1>
|
||||||
""" + med.__doc__ + """
|
""" + maskededit.__doc__ + """
|
||||||
</FONT></PRE>
|
</FONT></PRE>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ import sys
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import wx
|
import wx
|
||||||
import wx.lib.maskededit as me
|
from wx.lib import masked
|
||||||
import wx.lib.maskednumctrl as mnum
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
class TestPanel( wx.Panel ):
|
class TestPanel( wx.Panel ):
|
||||||
@ -16,40 +16,40 @@ class TestPanel( wx.Panel ):
|
|||||||
panel = wx.Panel( self, -1 )
|
panel = wx.Panel( self, -1 )
|
||||||
|
|
||||||
header = wx.StaticText(panel, -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.
|
The controls at the top reconfigure the resulting control at the bottom.
|
||||||
""")
|
""")
|
||||||
header.SetForegroundColour( "Blue" )
|
header.SetForegroundColour( "Blue" )
|
||||||
|
|
||||||
intlabel = wx.StaticText( panel, -1, "Integer width:" )
|
intlabel = wx.StaticText( panel, -1, "Integer width:" )
|
||||||
self.integerwidth = mnum.MaskedNumCtrl(
|
self.integerwidth = masked.NumCtrl(
|
||||||
panel, value=10, integerWidth=2, allowNegative=False
|
panel, value=10, integerWidth=2, allowNegative=False
|
||||||
)
|
)
|
||||||
|
|
||||||
fraclabel = wx.StaticText( panel, -1, "Fraction width:" )
|
fraclabel = wx.StaticText( panel, -1, "Fraction width:" )
|
||||||
self.fractionwidth = mnum.MaskedNumCtrl(
|
self.fractionwidth = masked.NumCtrl(
|
||||||
panel, value=0, integerWidth=2, allowNegative=False
|
panel, value=0, integerWidth=2, allowNegative=False
|
||||||
)
|
)
|
||||||
|
|
||||||
groupcharlabel = wx.StaticText( panel,-1, "Grouping char:" )
|
groupcharlabel = wx.StaticText( panel,-1, "Grouping char:" )
|
||||||
self.groupchar = me.MaskedTextCtrl(
|
self.groupchar = masked.TextCtrl(
|
||||||
panel, -1, value=',', mask='&', excludeChars = '-()',
|
panel, -1, value=',', mask='&', excludeChars = '-()',
|
||||||
formatcodes='F', emptyInvalid=True, validRequired=True
|
formatcodes='F', emptyInvalid=True, validRequired=True
|
||||||
)
|
)
|
||||||
|
|
||||||
decimalcharlabel = wx.StaticText( panel,-1, "Decimal char:" )
|
decimalcharlabel = wx.StaticText( panel,-1, "Decimal char:" )
|
||||||
self.decimalchar = me.MaskedTextCtrl(
|
self.decimalchar = masked.TextCtrl(
|
||||||
panel, -1, value='.', mask='&', excludeChars = '-()',
|
panel, -1, value='.', mask='&', excludeChars = '-()',
|
||||||
formatcodes='F', emptyInvalid=True, validRequired=True
|
formatcodes='F', emptyInvalid=True, validRequired=True
|
||||||
)
|
)
|
||||||
|
|
||||||
self.set_min = wx.CheckBox( panel, -1, "Set minimum value:" )
|
self.set_min = wx.CheckBox( panel, -1, "Set minimum value:" )
|
||||||
# Create this MaskedNumCtrl using factory, to show how:
|
# Create this masked.NumCtrl using factory, to show how:
|
||||||
self.min = mnum.MaskedNumCtrl( panel, integerWidth=5, fractionWidth=2 )
|
self.min = masked.Ctrl( panel, integerWidth=5, fractionWidth=2, controlType=masked.controlTypes.NUMBER )
|
||||||
self.min.Enable( False )
|
self.min.Enable( False )
|
||||||
|
|
||||||
self.set_max = wx.CheckBox( panel, -1, "Set maximum value:" )
|
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 )
|
self.max.Enable( False )
|
||||||
|
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ The controls at the top reconfigure the resulting control at the bottom.
|
|||||||
font.SetWeight(wx.BOLD)
|
font.SetWeight(wx.BOLD)
|
||||||
label.SetFont(font)
|
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, """\
|
label_numselect = wx.StaticText( panel, -1, """\
|
||||||
Programmatically set the above
|
Programmatically set the above
|
||||||
@ -141,15 +141,15 @@ value entry ctrl:""")
|
|||||||
panel.Move( (50,10) )
|
panel.Move( (50,10) )
|
||||||
self.panel = panel
|
self.panel = panel
|
||||||
|
|
||||||
self.Bind(mnum.EVT_MASKEDNUM, self.OnSetIntWidth, self.integerwidth )
|
self.Bind(masked.EVT_NUM, self.OnSetIntWidth, self.integerwidth )
|
||||||
self.Bind(mnum.EVT_MASKEDNUM, self.OnSetFractionWidth, self.fractionwidth )
|
self.Bind(masked.EVT_NUM, self.OnSetFractionWidth, self.fractionwidth )
|
||||||
self.Bind(wx.EVT_TEXT, self.OnSetGroupChar, self.groupchar )
|
self.Bind(wx.EVT_TEXT, self.OnSetGroupChar, self.groupchar )
|
||||||
self.Bind(wx.EVT_TEXT, self.OnSetDecimalChar, self.decimalchar )
|
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.OnSetMin, self.set_min )
|
||||||
self.Bind(wx.EVT_CHECKBOX, self.OnSetMax, self.set_max )
|
self.Bind(wx.EVT_CHECKBOX, self.OnSetMax, self.set_max )
|
||||||
self.Bind(mnum.EVT_MASKEDNUM, self.SetTargetMinMax, self.min )
|
self.Bind(masked.EVT_NUM, self.SetTargetMinMax, self.min )
|
||||||
self.Bind(mnum.EVT_MASKEDNUM, self.SetTargetMinMax, self.max )
|
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.SetTargetMinMax, self.limit_target )
|
||||||
self.Bind(wx.EVT_CHECKBOX, self.OnSetAllowNone, self.allow_none )
|
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.OnSetUseParens, self.use_parens )
|
||||||
self.Bind(wx.EVT_CHECKBOX, self.OnSetSelectOnEntry, self.select_on_entry )
|
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 )
|
self.Bind(wx.EVT_COMBOBOX, self.OnNumberSelect, self.numselect )
|
||||||
|
|
||||||
|
|
||||||
@ -323,6 +323,7 @@ def runTest( frame, nb, log ):
|
|||||||
return win
|
return win
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
import wx.lib.masked.numctrl as mnum
|
||||||
overview = mnum.__doc__
|
overview = mnum.__doc__
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import wx
|
import wx
|
||||||
import wx.lib.timectrl as timectl
|
|
||||||
import wx.lib.scrolledpanel as scrolled
|
import wx.lib.scrolledpanel as scrolled
|
||||||
|
import wx.lib.masked as masked
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
@ -18,19 +18,19 @@ class TestPanel( scrolled.ScrolledPanel ):
|
|||||||
|
|
||||||
|
|
||||||
text1 = wx.StaticText( self, -1, "12-hour format:")
|
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 )
|
spin1 = wx.SpinButton( self, -1, wx.DefaultPosition, (-1,20), 0 )
|
||||||
self.time12.BindSpinButton( spin1 )
|
self.time12.BindSpinButton( spin1 )
|
||||||
|
|
||||||
text2 = wx.StaticText( self, -1, "24-hour format:")
|
text2 = wx.StaticText( self, -1, "24-hour format:")
|
||||||
spin2 = wx.SpinButton( self, -1, wx.DefaultPosition, (-1,20), 0 )
|
spin2 = wx.SpinButton( self, -1, wx.DefaultPosition, (-1,20), 0 )
|
||||||
self.time24 = timectl.TimeCtrl(
|
self.time24 = masked.TimeCtrl(
|
||||||
self, -1, name="24 hour control", fmt24hr=True,
|
self, -1, name="24 hour control", fmt24hr=True,
|
||||||
spinButton = spin2
|
spinButton = spin2
|
||||||
)
|
)
|
||||||
|
|
||||||
text3 = wx.StaticText( self, -1, "No seconds\nor spin button:")
|
text3 = wx.StaticText( self, -1, "No seconds\nor spin button:")
|
||||||
self.spinless_ctrl = timectl.TimeCtrl(
|
self.spinless_ctrl = masked.TimeCtrl(
|
||||||
self, -1, name="spinless control",
|
self, -1, name="spinless control",
|
||||||
display_seconds = False
|
display_seconds = False
|
||||||
)
|
)
|
||||||
@ -86,17 +86,17 @@ class TestPanel( scrolled.ScrolledPanel ):
|
|||||||
self.set_bounds = wx.CheckBox( self, -1, "Set time bounds:" )
|
self.set_bounds = wx.CheckBox( self, -1, "Set time bounds:" )
|
||||||
|
|
||||||
minlabel = wx.StaticText( self, -1, "minimum time:" )
|
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 )
|
self.min.Enable( False )
|
||||||
|
|
||||||
maxlabel = wx.StaticText( self, -1, "maximum time:" )
|
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.max.Enable( False )
|
||||||
|
|
||||||
self.limit_check = wx.CheckBox( self, -1, "Limit control" )
|
self.limit_check = wx.CheckBox( self, -1, "Limit control" )
|
||||||
|
|
||||||
label = wx.StaticText( self, -1, "Resulting time 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 = wx.FlexGridSizer( 0, 2, 0, 0 )
|
||||||
grid2.Add( (20, 0), 0, wx.ALIGN_LEFT|wx.ALL, 5 )
|
grid2.Add( (20, 0), 0, wx.ALIGN_LEFT|wx.ALL, 5 )
|
||||||
@ -142,14 +142,14 @@ class TestPanel( scrolled.ScrolledPanel ):
|
|||||||
self.SetupScrolling()
|
self.SetupScrolling()
|
||||||
|
|
||||||
self.Bind(wx.EVT_BUTTON, self.OnButtonClick, buttonChange )
|
self.Bind(wx.EVT_BUTTON, self.OnButtonClick, buttonChange )
|
||||||
self.Bind(timectl.EVT_TIMEUPDATE, self.OnTimeChange, self.time12 )
|
self.Bind(masked.EVT_TIMEUPDATE, self.OnTimeChange, self.time12 )
|
||||||
self.Bind(timectl.EVT_TIMEUPDATE, self.OnTimeChange, self.time24 )
|
self.Bind(masked.EVT_TIMEUPDATE, self.OnTimeChange, self.time24 )
|
||||||
self.Bind(timectl.EVT_TIMEUPDATE, self.OnTimeChange, self.spinless_ctrl )
|
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.OnBoundsCheck, self.set_bounds )
|
||||||
self.Bind(wx.EVT_CHECKBOX, self.SetTargetMinMax, self.limit_check )
|
self.Bind(wx.EVT_CHECKBOX, self.SetTargetMinMax, self.limit_check )
|
||||||
self.Bind(timectl.EVT_TIMEUPDATE, self.SetTargetMinMax, self.min )
|
self.Bind(masked.EVT_TIMEUPDATE, self.SetTargetMinMax, self.min )
|
||||||
self.Bind(timectl.EVT_TIMEUPDATE, self.SetTargetMinMax, self.max )
|
self.Bind(masked.EVT_TIMEUPDATE, self.SetTargetMinMax, self.max )
|
||||||
self.Bind(timectl.EVT_TIMEUPDATE, self.OnTimeChange, self.target_ctrl )
|
self.Bind(masked.EVT_TIMEUPDATE, self.OnTimeChange, self.target_ctrl )
|
||||||
|
|
||||||
|
|
||||||
def OnTimeChange( self, event ):
|
def OnTimeChange( self, event ):
|
||||||
@ -204,7 +204,7 @@ class TestPanel( scrolled.ScrolledPanel ):
|
|||||||
min, max = None, None
|
min, max = None, None
|
||||||
|
|
||||||
cur_min, cur_max = self.target_ctrl.GetBounds()
|
cur_min, cur_max = self.target_ctrl.GetBounds()
|
||||||
|
print cur_min, min
|
||||||
if min and (min != cur_min): self.target_ctrl.SetMin( min )
|
if min and (min != cur_min): self.target_ctrl.SetMin( min )
|
||||||
if max and (max != cur_max): self.target_ctrl.SetMax( max )
|
if max and (max != cur_max): self.target_ctrl.SetMax( max )
|
||||||
|
|
||||||
@ -225,11 +225,11 @@ def runTest( frame, nb, log ):
|
|||||||
return win
|
return win
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
import wx.lib.masked.timectrl as timectl
|
||||||
overview = timectl.__doc__
|
overview = timectl.__doc__
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys,os
|
import sys,os
|
||||||
import run
|
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)
|
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.
|
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
|
GetSystemColour, GetSystemFont and GetSystemMetric have also gone into
|
||||||
the bit-bucket. Use GetColour, GetFont and GetMetric instead.
|
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
|
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
|
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
|
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
|
convert itself to one. A similar conversion fragment is in place for
|
||||||
parameters that expect floating point values.
|
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
|
# Author: Will Sadkin
|
||||||
# Created: 09/24/2003
|
# Created: 09/24/2003
|
||||||
# Copyright: (c) 2003 by Will Sadkin
|
# Copyright: (c) 2003 by Will Sadkin
|
||||||
@ -17,24 +17,24 @@
|
|||||||
|
|
||||||
"""<html><body>
|
"""<html><body>
|
||||||
<P>
|
<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:
|
masked edit controls:
|
||||||
<P>
|
<P>
|
||||||
<UL>
|
<UL>
|
||||||
<LI><b>MaskedTextCtrl</b> - standard masked edit text box</LI>
|
<LI><b>masked.TextCtrl</b> - standard masked edit text box</LI>
|
||||||
<LI><b>MaskedComboBox</b> - adds combobox capabilities</LI>
|
<LI><b>masked.ComboBox</b> - adds combobox capabilities</LI>
|
||||||
<LI><b>IpAddrCtrl</b> - adds logical input semantics for IP address entry</LI>
|
<LI><b>masked.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>masked.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.NumCtrl</b> - special subclass handling numeric values</LI>
|
||||||
</UL>
|
</UL>
|
||||||
<P>
|
<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
|
parameter in the variable arguments of the control, to determine
|
||||||
what kind of instance to return.
|
what kind of instance to return.
|
||||||
controlType can be one of:
|
controlType can be one of:
|
||||||
<PRE><FONT SIZE=-1>
|
<PRE><FONT SIZE=-1>
|
||||||
controlTypes.MASKEDTEXT
|
controlTypes.TEXT
|
||||||
controlTypes.MASKEDCOMBO
|
controlTypes.COMBO
|
||||||
controlTypes.IPADDR
|
controlTypes.IPADDR
|
||||||
controlTypes.TIME
|
controlTypes.TIME
|
||||||
controlTypes.NUMBER
|
controlTypes.NUMBER
|
||||||
@ -42,56 +42,56 @@ controlType can be one of:
|
|||||||
These constants are also available individually, ie, you can
|
These constants are also available individually, ie, you can
|
||||||
use either of the following:
|
use either of the following:
|
||||||
<PRE><FONT SIZE=-1>
|
<PRE><FONT SIZE=-1>
|
||||||
from wxPython.wx.lib.maskedctrl import MaskedCtrl, MASKEDCOMBO, MASKEDTEXT, NUMBER
|
from wxPython.wx.lib.masked import Ctrl, COMBO, TEXT, NUMBER, TIME
|
||||||
from wxPython.wx.lib.maskedctrl import MaskedCtrl, controlTypes
|
from wxPython.wx.lib.masked import Ctrl, controlTypes
|
||||||
</FONT></PRE>
|
</FONT></PRE>
|
||||||
If not specified as a keyword argument, the default controlType is
|
If not specified as a keyword argument, the default controlType is
|
||||||
controlTypes.MASKEDTEXT.
|
controlTypes.TEXT.
|
||||||
<P>
|
<P>
|
||||||
Each of the above classes has its own unique arguments, but MaskedCtrl
|
Each of the above classes has its own unique arguments, but MaskedCtrl
|
||||||
provides a single "unified" interface for masked controls. MaskedTextCtrl,
|
provides a single "unified" interface for masked controls. Masked.TextCtrl,
|
||||||
MaskedComboBox and IpAddrCtrl are all documented below; the others have
|
masked.ComboBox and masked.IpAddrCtrl are all documented below; the others have
|
||||||
their own demo pages and interface descriptions.
|
their own demo pages and interface descriptions.
|
||||||
</body></html>
|
</body></html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from wx.lib.maskededit import MaskedTextCtrl, MaskedComboBox, IpAddrCtrl
|
from wx.lib.masked import TextCtrl, ComboBox, IpAddrCtrl
|
||||||
from wx.lib.maskednumctrl import MaskedNumCtrl
|
from wx.lib.masked import NumCtrl
|
||||||
from wx.lib.timectrl import TimeCtrl
|
from wx.lib.masked import TimeCtrl
|
||||||
|
|
||||||
|
|
||||||
# "type" enumeration for class instance factory function
|
# "type" enumeration for class instance factory function
|
||||||
MASKEDTEXT = 0
|
TEXT = 0
|
||||||
MASKEDCOMBO = 1
|
COMBO = 1
|
||||||
IPADDR = 2
|
IPADDR = 2
|
||||||
TIME = 3
|
TIME = 3
|
||||||
NUMBER = 4
|
NUMBER = 4
|
||||||
|
|
||||||
# for ease of import
|
# for ease of import
|
||||||
class controlTypes:
|
class controlTypes:
|
||||||
MASKEDTEXT = MASKEDTEXT
|
TEXT = TEXT
|
||||||
MASKEDCOMBO = MASKEDCOMBO
|
COMBO = COMBO
|
||||||
IPADDR = IPADDR
|
IPADDR = IPADDR
|
||||||
TIME = TIME
|
TIME = TIME
|
||||||
NUMBER = NUMBER
|
NUMBER = NUMBER
|
||||||
|
|
||||||
|
|
||||||
def MaskedCtrl( *args, **kwargs):
|
def Ctrl( *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Actually a factory function providing a unifying
|
Actually a factory function providing a unifying
|
||||||
interface for generating masked controls.
|
interface for generating masked controls.
|
||||||
"""
|
"""
|
||||||
if not kwargs.has_key('controlType'):
|
if not kwargs.has_key('controlType'):
|
||||||
controlType = MASKEDTEXT
|
controlType = TEXT
|
||||||
else:
|
else:
|
||||||
controlType = kwargs['controlType']
|
controlType = kwargs['controlType']
|
||||||
del kwargs['controlType']
|
del kwargs['controlType']
|
||||||
|
|
||||||
if controlType == MASKEDTEXT:
|
if controlType == TEXT:
|
||||||
return MaskedTextCtrl(*args, **kwargs)
|
return TextCtrl(*args, **kwargs)
|
||||||
|
|
||||||
elif controlType == MASKEDCOMBO:
|
elif controlType == COMBO:
|
||||||
return MaskedComboBox(*args, **kwargs)
|
return ComboBox(*args, **kwargs)
|
||||||
|
|
||||||
elif controlType == IPADDR:
|
elif controlType == IPADDR:
|
||||||
return IpAddrCtrl(*args, **kwargs)
|
return IpAddrCtrl(*args, **kwargs)
|
||||||
@ -100,7 +100,7 @@ def MaskedCtrl( *args, **kwargs):
|
|||||||
return TimeCtrl(*args, **kwargs)
|
return TimeCtrl(*args, **kwargs)
|
||||||
|
|
||||||
elif controlType == NUMBER:
|
elif controlType == NUMBER:
|
||||||
return MaskedNumCtrl(*args, **kwargs)
|
return NumCtrl(*args, **kwargs)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise AttributeError(
|
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
|
# Author: Will Sadkin
|
||||||
# Created: 09/06/2003
|
# Created: 09/06/2003
|
||||||
# Copyright: (c) 2003 by Will Sadkin
|
# Copyright: (c) 2003 by Will Sadkin
|
||||||
@ -9,12 +9,12 @@
|
|||||||
# NOTE:
|
# NOTE:
|
||||||
# This was written to provide a numeric edit control for wxPython that
|
# This was written to provide a numeric edit control for wxPython that
|
||||||
# does things like right-insert (like a calculator), and does grouping, etc.
|
# 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.
|
# 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
|
# 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.
|
# changes to the control.
|
||||||
#
|
#
|
||||||
# It supports negative numbers as well as the naturals, and has the option
|
# It supports negative numbers as well as the naturals, and has the option
|
||||||
@ -24,11 +24,11 @@
|
|||||||
# Similarly, replacing the contents of the control with '-' will result in
|
# Similarly, replacing the contents of the control with '-' will result in
|
||||||
# a selected (absolute) value of -1.
|
# 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
|
# enforcing them or simply coloring the text of the control if the limits
|
||||||
# are exceeded.
|
# 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
|
# is derived from BaseMaskedTextCtrl. As such, it supports a limited range
|
||||||
# of values to comply with a fixed-width entry mask.
|
# of values to comply with a fixed-width entry mask.
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
@ -39,14 +39,14 @@
|
|||||||
# 12/20/2003 - Jeff Grimmett (grimmtooth@softhome.net)
|
# 12/20/2003 - Jeff Grimmett (grimmtooth@softhome.net)
|
||||||
#
|
#
|
||||||
# o wxMaskedEditMixin -> MaskedEditMixin
|
# o wxMaskedEditMixin -> MaskedEditMixin
|
||||||
# o wxMaskedTextCtrl -> MaskedTextCtrl
|
# o wxMaskedTextCtrl -> masked.TextCtrl
|
||||||
# o wxMaskedNumNumberUpdatedEvent -> MaskedNumNumberUpdatedEvent
|
# o wxMaskedNumNumberUpdatedEvent -> masked.NumberUpdatedEvent
|
||||||
# o wxMaskedNumCtrl -> MaskedNumCtrl
|
# o wxMaskedNumCtrl -> masked.NumCtrl
|
||||||
#
|
#
|
||||||
|
|
||||||
"""<html><body>
|
"""<html><body>
|
||||||
<P>
|
<P>
|
||||||
<B>MaskedNumCtrl:</B>
|
<B>masked.NumCtrl:</B>
|
||||||
<UL>
|
<UL>
|
||||||
<LI>allows you to get and set integer or floating point numbers as value,</LI>
|
<LI>allows you to get and set integer or floating point numbers as value,</LI>
|
||||||
<LI>provides bounds support and optional value limiting,</LI>
|
<LI>provides bounds support and optional value limiting,</LI>
|
||||||
@ -62,14 +62,14 @@ fractional portion.
|
|||||||
<P>
|
<P>
|
||||||
Here's the API:
|
Here's the API:
|
||||||
<DL><PRE>
|
<DL><PRE>
|
||||||
<B>MaskedNumCtrl</B>(
|
<B>masked.NumCtrl</B>(
|
||||||
parent, id = -1,
|
parent, id = -1,
|
||||||
<B>value</B> = 0,
|
<B>value</B> = 0,
|
||||||
pos = wx.DefaultPosition,
|
pos = wx.DefaultPosition,
|
||||||
size = wx.DefaultSize,
|
size = wx.DefaultSize,
|
||||||
style = 0,
|
style = 0,
|
||||||
validator = wx.DefaultValidator,
|
validator = wx.DefaultValidator,
|
||||||
name = "maskednumber",
|
name = "masked.number",
|
||||||
<B>integerWidth</B> = 10,
|
<B>integerWidth</B> = 10,
|
||||||
<B>fractionWidth</B> = 0,
|
<B>fractionWidth</B> = 0,
|
||||||
<B>allowNone</B> = False,
|
<B>allowNone</B> = False,
|
||||||
@ -190,7 +190,7 @@ Here's the API:
|
|||||||
</UL>
|
</UL>
|
||||||
<BR>
|
<BR>
|
||||||
<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
|
<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
|
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
|
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
|
MININT = -maxint-1
|
||||||
|
|
||||||
from wx.tools.dbg import Logger
|
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 = Logger()
|
||||||
dbg(enable=0)
|
##dbg(enable=0)
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
|
|
||||||
wxEVT_COMMAND_MASKED_NUMBER_UPDATED = wx.NewEventType()
|
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):
|
def __init__(self, id, value = 0, object=None):
|
||||||
wx.PyCommandEvent.__init__(self, wxEVT_COMMAND_MASKED_NUMBER_UPDATED, id)
|
wx.PyCommandEvent.__init__(self, wxEVT_COMMAND_MASKED_NUMBER_UPDATED, id)
|
||||||
|
|
||||||
@ -409,8 +409,8 @@ class MaskedNumNumberUpdatedEvent(wx.PyCommandEvent):
|
|||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
class MaskedNumCtrlAccessorsMixin:
|
class NumCtrlAccessorsMixin:
|
||||||
# Define wxMaskedNumCtrl's list of attributes having their own
|
# Define masked.NumCtrl's list of attributes having their own
|
||||||
# Get/Set functions, ignoring those that make no sense for
|
# Get/Set functions, ignoring those that make no sense for
|
||||||
# an numeric control.
|
# an numeric control.
|
||||||
exposed_basectrl_params = (
|
exposed_basectrl_params = (
|
||||||
@ -448,7 +448,7 @@ class MaskedNumCtrlAccessorsMixin:
|
|||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
|
|
||||||
class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
|
||||||
|
|
||||||
|
|
||||||
valid_ctrl_params = {
|
valid_ctrl_params = {
|
||||||
@ -478,31 +478,32 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
self, parent, id=-1, value = 0,
|
self, parent, id=-1, value = 0,
|
||||||
pos = wx.DefaultPosition, size = wx.DefaultSize,
|
pos = wx.DefaultPosition, size = wx.DefaultSize,
|
||||||
style = wx.TE_PROCESS_TAB, validator = wx.DefaultValidator,
|
style = wx.TE_PROCESS_TAB, validator = wx.DefaultValidator,
|
||||||
name = "maskednum",
|
name = "masked.num",
|
||||||
**kwargs ):
|
**kwargs ):
|
||||||
|
|
||||||
dbg('MaskedNumCtrl::__init__', indent=1)
|
## dbg('masked.NumCtrl::__init__', indent=1)
|
||||||
|
|
||||||
# Set defaults for control:
|
# Set defaults for control:
|
||||||
dbg('setting defaults:')
|
## dbg('setting defaults:')
|
||||||
for key, param_value in MaskedNumCtrl.valid_ctrl_params.items():
|
for key, param_value in NumCtrl.valid_ctrl_params.items():
|
||||||
# This is done this way to make setattr behave consistently with
|
# This is done this way to make setattr behave consistently with
|
||||||
# "private attribute" name mangling
|
# "private attribute" name mangling
|
||||||
setattr(self, '_' + key, copy.copy(param_value))
|
setattr(self, '_' + key, copy.copy(param_value))
|
||||||
|
|
||||||
# Assign defaults for all attributes:
|
# Assign defaults for all attributes:
|
||||||
init_args = copy.deepcopy(MaskedNumCtrl.valid_ctrl_params)
|
init_args = copy.deepcopy(NumCtrl.valid_ctrl_params)
|
||||||
dbg('kwargs:', kwargs)
|
## dbg('kwargs:', kwargs)
|
||||||
for key, param_value in kwargs.items():
|
for key, param_value in kwargs.items():
|
||||||
key = key.replace('Color', 'Colour')
|
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)
|
raise AttributeError('invalid keyword argument "%s"' % key)
|
||||||
else:
|
else:
|
||||||
init_args[key] = param_value
|
init_args[key] = param_value
|
||||||
dbg('init_args:', indent=1)
|
## dbg('init_args:', indent=1)
|
||||||
for key, param_value in init_args.items():
|
for key, param_value in init_args.items():
|
||||||
dbg('%s:' % key, param_value)
|
## dbg('%s:' % key, param_value)
|
||||||
dbg(indent=0)
|
pass
|
||||||
|
## dbg(indent=0)
|
||||||
|
|
||||||
# Process initial fields for the control, as part of construction:
|
# Process initial fields for the control, as part of construction:
|
||||||
if type(init_args['integerWidth']) != types.IntType:
|
if type(init_args['integerWidth']) != types.IntType:
|
||||||
@ -521,7 +522,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
|
|
||||||
if self._fractionWidth:
|
if self._fractionWidth:
|
||||||
fracmask = '.' + '#{%d}' % self._fractionWidth
|
fracmask = '.' + '#{%d}' % self._fractionWidth
|
||||||
dbg('fracmask:', fracmask)
|
## dbg('fracmask:', fracmask)
|
||||||
fields[1] = Field(defaultValue='0'*self._fractionWidth)
|
fields[1] = Field(defaultValue='0'*self._fractionWidth)
|
||||||
else:
|
else:
|
||||||
fracmask = ''
|
fracmask = ''
|
||||||
@ -537,7 +538,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
else:
|
else:
|
||||||
emptyInvalid = True
|
emptyInvalid = True
|
||||||
fields[0] = Field(formatcodes='r<>', emptyInvalid=emptyInvalid)
|
fields[0] = Field(formatcodes='r<>', emptyInvalid=emptyInvalid)
|
||||||
dbg('intmask:', intmask)
|
## dbg('intmask:', intmask)
|
||||||
|
|
||||||
# don't bother to reprocess these arguments:
|
# don't bother to reprocess these arguments:
|
||||||
del init_args['integerWidth']
|
del init_args['integerWidth']
|
||||||
@ -585,14 +586,14 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
|
|
||||||
# Ensure proper coloring:
|
# Ensure proper coloring:
|
||||||
self.Refresh()
|
self.Refresh()
|
||||||
dbg('finished MaskedNumCtrl::__init__', indent=0)
|
## dbg('finished NumCtrl::__init__', indent=0)
|
||||||
|
|
||||||
|
|
||||||
def SetParameters(self, **kwargs):
|
def SetParameters(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
This routine is used to initialize and reconfigure the control:
|
This routine is used to initialize and reconfigure the control:
|
||||||
"""
|
"""
|
||||||
dbg('MaskedNumCtrl::SetParameters', indent=1)
|
## dbg('NumCtrl::SetParameters', indent=1)
|
||||||
maskededit_kwargs = {}
|
maskededit_kwargs = {}
|
||||||
reset_fraction_width = False
|
reset_fraction_width = False
|
||||||
|
|
||||||
@ -620,14 +621,14 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
else:
|
else:
|
||||||
emptyInvalid = True
|
emptyInvalid = True
|
||||||
fracmask = ''
|
fracmask = ''
|
||||||
dbg('fracmask:', fracmask)
|
## dbg('fracmask:', fracmask)
|
||||||
|
|
||||||
if kwargs.has_key('integerWidth'):
|
if kwargs.has_key('integerWidth'):
|
||||||
if type(kwargs['integerWidth']) != types.IntType:
|
if type(kwargs['integerWidth']) != types.IntType:
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
raise AttributeError('invalid integerWidth (%s) specified; expected integer' % repr(kwargs['integerWidth']))
|
raise AttributeError('invalid integerWidth (%s) specified; expected integer' % repr(kwargs['integerWidth']))
|
||||||
elif kwargs['integerWidth'] < 0:
|
elif kwargs['integerWidth'] < 0:
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
raise AttributeError('invalid integerWidth (%s) specified; must be > 0' % repr(kwargs['integerWidth']))
|
raise AttributeError('invalid integerWidth (%s) specified; must be > 0' % repr(kwargs['integerWidth']))
|
||||||
else:
|
else:
|
||||||
self._integerWidth = kwargs['integerWidth']
|
self._integerWidth = kwargs['integerWidth']
|
||||||
@ -641,7 +642,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
self._groupSpace = 0
|
self._groupSpace = 0
|
||||||
|
|
||||||
intmask = '#{%d}' % (self._integerWidth + self._groupSpace)
|
intmask = '#{%d}' % (self._integerWidth + self._groupSpace)
|
||||||
dbg('intmask:', intmask)
|
## dbg('intmask:', intmask)
|
||||||
fields[0] = Field(formatcodes='r<>', emptyInvalid=emptyInvalid)
|
fields[0] = Field(formatcodes='r<>', emptyInvalid=emptyInvalid)
|
||||||
maskededit_kwargs['fields'] = fields
|
maskededit_kwargs['fields'] = fields
|
||||||
|
|
||||||
@ -655,17 +656,17 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
|
|
||||||
if kwargs.has_key('groupChar'):
|
if kwargs.has_key('groupChar'):
|
||||||
old_groupchar = self._groupChar # save so we can reformat properly
|
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']
|
maskededit_kwargs['groupChar'] = kwargs['groupChar']
|
||||||
if kwargs.has_key('decimalChar'):
|
if kwargs.has_key('decimalChar'):
|
||||||
old_decimalchar = self._decimalChar
|
old_decimalchar = self._decimalChar
|
||||||
dbg("old_decimalchar: '%s'" % old_decimalchar)
|
## dbg("old_decimalchar: '%s'" % old_decimalchar)
|
||||||
maskededit_kwargs['decimalChar'] = kwargs['decimalChar']
|
maskededit_kwargs['decimalChar'] = kwargs['decimalChar']
|
||||||
|
|
||||||
# for all other parameters, assign keyword args as appropriate:
|
# for all other parameters, assign keyword args as appropriate:
|
||||||
for key, param_value in kwargs.items():
|
for key, param_value in kwargs.items():
|
||||||
key = key.replace('Color', 'Colour')
|
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)
|
raise AttributeError('invalid keyword argument "%s"' % key)
|
||||||
elif key not in MaskedEditMixin.valid_ctrl_params.keys():
|
elif key not in MaskedEditMixin.valid_ctrl_params.keys():
|
||||||
setattr(self, '_' + key, param_value)
|
setattr(self, '_' + key, param_value)
|
||||||
@ -673,7 +674,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
raise AttributeError('invalid keyword argument "%s"' % key)
|
raise AttributeError('invalid keyword argument "%s"' % key)
|
||||||
else:
|
else:
|
||||||
maskededit_kwargs[key] = param_value
|
maskededit_kwargs[key] = param_value
|
||||||
dbg('kwargs:', kwargs)
|
## dbg('kwargs:', kwargs)
|
||||||
|
|
||||||
# reprocess existing format codes to ensure proper resulting format:
|
# reprocess existing format codes to ensure proper resulting format:
|
||||||
formatcodes = self.GetCtrlParameter('formatcodes')
|
formatcodes = self.GetCtrlParameter('formatcodes')
|
||||||
@ -695,7 +696,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
|
|
||||||
if kwargs.has_key('selectOnEntry'):
|
if kwargs.has_key('selectOnEntry'):
|
||||||
self._selectOnEntry = kwargs['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:
|
if kwargs['selectOnEntry'] and 'S' not in formatcodes:
|
||||||
formatcodes += 'S'
|
formatcodes += 'S'
|
||||||
maskededit_kwargs['formatcodes'] = formatcodes
|
maskededit_kwargs['formatcodes'] = formatcodes
|
||||||
@ -728,7 +729,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
maskededit_kwargs['validRequired'] = False
|
maskededit_kwargs['validRequired'] = False
|
||||||
self._limited = kwargs['limited']
|
self._limited = kwargs['limited']
|
||||||
|
|
||||||
dbg('maskededit_kwargs:', maskededit_kwargs)
|
## dbg('maskededit_kwargs:', maskededit_kwargs)
|
||||||
if maskededit_kwargs.keys():
|
if maskededit_kwargs.keys():
|
||||||
self.SetCtrlParameters(**maskededit_kwargs)
|
self.SetCtrlParameters(**maskededit_kwargs)
|
||||||
|
|
||||||
@ -756,17 +757,18 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
if( self._max is None
|
if( self._max is None
|
||||||
or min is None
|
or min is None
|
||||||
or (self._max is not None and self._max >= min) ):
|
or (self._max is not None and self._max >= min) ):
|
||||||
dbg('examining min')
|
## dbg('examining min')
|
||||||
if min is not None:
|
if min is not None:
|
||||||
try:
|
try:
|
||||||
textmin = self._toGUI(min, apply_limits = False)
|
textmin = self._toGUI(min, apply_limits = False)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
dbg('min will not fit into control; ignoring', indent=0)
|
## dbg('min will not fit into control; ignoring', indent=0)
|
||||||
raise
|
raise
|
||||||
dbg('accepted min')
|
## dbg('accepted min')
|
||||||
self._min = min
|
self._min = min
|
||||||
else:
|
else:
|
||||||
dbg('ignoring min')
|
## dbg('ignoring min')
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
if kwargs.has_key('max'):
|
if kwargs.has_key('max'):
|
||||||
@ -774,24 +776,25 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
if( self._min is None
|
if( self._min is None
|
||||||
or max is None
|
or max is None
|
||||||
or (self._min is not None and self._min <= max) ):
|
or (self._min is not None and self._min <= max) ):
|
||||||
dbg('examining max')
|
## dbg('examining max')
|
||||||
if max is not None:
|
if max is not None:
|
||||||
try:
|
try:
|
||||||
textmax = self._toGUI(max, apply_limits = False)
|
textmax = self._toGUI(max, apply_limits = False)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
dbg('max will not fit into control; ignoring', indent=0)
|
## dbg('max will not fit into control; ignoring', indent=0)
|
||||||
raise
|
raise
|
||||||
dbg('accepted max')
|
## dbg('accepted max')
|
||||||
self._max = max
|
self._max = max
|
||||||
else:
|
else:
|
||||||
dbg('ignoring max')
|
## dbg('ignoring max')
|
||||||
|
pass
|
||||||
|
|
||||||
if kwargs.has_key('allowNegative'):
|
if kwargs.has_key('allowNegative'):
|
||||||
self._allowNegative = kwargs['allowNegative']
|
self._allowNegative = kwargs['allowNegative']
|
||||||
|
|
||||||
# Ensure current value of control obeys any new restrictions imposed:
|
# Ensure current value of control obeys any new restrictions imposed:
|
||||||
text = self._GetValue()
|
text = self._GetValue()
|
||||||
dbg('text value: "%s"' % text)
|
## dbg('text value: "%s"' % text)
|
||||||
if kwargs.has_key('groupChar') and text.find(old_groupchar) != -1:
|
if kwargs.has_key('groupChar') and text.find(old_groupchar) != -1:
|
||||||
text = text.replace(old_groupchar, self._groupChar)
|
text = text.replace(old_groupchar, self._groupChar)
|
||||||
if kwargs.has_key('decimalChar') and text.find(old_decimalchar) != -1:
|
if kwargs.has_key('decimalChar') and text.find(old_decimalchar) != -1:
|
||||||
@ -801,10 +804,10 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
|
|
||||||
value = self.GetValue()
|
value = self.GetValue()
|
||||||
|
|
||||||
dbg('self._allowNegative?', self._allowNegative)
|
## dbg('self._allowNegative?', self._allowNegative)
|
||||||
if not self._allowNegative and self._isNeg:
|
if not self._allowNegative and self._isNeg:
|
||||||
value = abs(value)
|
value = abs(value)
|
||||||
dbg('abs(value):', value)
|
## dbg('abs(value):', value)
|
||||||
self._isNeg = False
|
self._isNeg = False
|
||||||
|
|
||||||
elif not self._allowNone and BaseMaskedTextCtrl.GetValue(self) == '':
|
elif not self._allowNone and BaseMaskedTextCtrl.GetValue(self) == '':
|
||||||
@ -815,19 +818,19 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
|
|
||||||
sel_start, sel_to = self.GetSelection()
|
sel_start, sel_to = self.GetSelection()
|
||||||
if self.IsLimited() and self._min is not None and value < self._min:
|
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))
|
self._SetValue(self._toGUI(self._min))
|
||||||
|
|
||||||
elif self.IsLimited() and self._max is not None and value > self._max:
|
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))
|
self._SetValue(self._toGUI(self._max))
|
||||||
else:
|
else:
|
||||||
# reformat current value as appropriate to possibly new conditions
|
# reformat current value as appropriate to possibly new conditions
|
||||||
dbg('Reformatting value:', value)
|
## dbg('Reformatting value:', value)
|
||||||
sel_start, sel_to = self.GetSelection()
|
sel_start, sel_to = self.GetSelection()
|
||||||
self._SetValue(self._toGUI(value))
|
self._SetValue(self._toGUI(value))
|
||||||
self.Refresh() # recolor as appropriate
|
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)
|
return string.atof(fracstring)
|
||||||
|
|
||||||
def _OnChangeSign(self, event):
|
def _OnChangeSign(self, event):
|
||||||
dbg('MaskedNumCtrl::_OnChangeSign', indent=1)
|
## dbg('NumCtrl::_OnChangeSign', indent=1)
|
||||||
self._typedSign = True
|
self._typedSign = True
|
||||||
MaskedEditMixin._OnChangeSign(self, event)
|
MaskedEditMixin._OnChangeSign(self, event)
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
|
|
||||||
|
|
||||||
def _disallowValue(self):
|
def _disallowValue(self):
|
||||||
dbg('MaskedNumCtrl::_disallowValue')
|
## dbg('NumCtrl::_disallowValue')
|
||||||
# limited and -1 is out of bounds
|
# limited and -1 is out of bounds
|
||||||
if self._typedSign:
|
if self._typedSign:
|
||||||
self._isNeg = False
|
self._isNeg = False
|
||||||
if not wx.Validator_IsSilent():
|
if not wx.Validator_IsSilent():
|
||||||
wx.Bell()
|
wx.Bell()
|
||||||
sel_start, sel_to = self._GetSelection()
|
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.SetInsertionPoint, sel_start) # preserve current selection/position
|
||||||
wx.CallAfter(self.SetSelection, sel_start, sel_to)
|
wx.CallAfter(self.SetSelection, sel_start, sel_to)
|
||||||
|
|
||||||
@ -886,19 +889,19 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
by the user.
|
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
|
if( (self._fractionWidth and value.find(self._decimalChar) == -1) or
|
||||||
(self._fractionWidth == 0 and value.find(self._decimalChar) != -1) ) :
|
(self._fractionWidth == 0 and value.find(self._decimalChar) != -1) ) :
|
||||||
value = self._toGUI(value)
|
value = self._toGUI(value)
|
||||||
|
|
||||||
numvalue = self._GetNumValue(value)
|
numvalue = self._GetNumValue(value)
|
||||||
dbg('cleansed value: "%s"' % numvalue)
|
## dbg('cleansed value: "%s"' % numvalue)
|
||||||
replacement = None
|
replacement = None
|
||||||
|
|
||||||
if numvalue == "":
|
if numvalue == "":
|
||||||
if self._allowNone:
|
if self._allowNone:
|
||||||
dbg('calling base BaseMaskedTextCtrl._SetValue(self, "%s")' % value)
|
## dbg('calling base BaseMaskedTextCtrl._SetValue(self, "%s")' % value)
|
||||||
BaseMaskedTextCtrl._SetValue(self, value)
|
BaseMaskedTextCtrl._SetValue(self, value)
|
||||||
self.Refresh()
|
self.Refresh()
|
||||||
return
|
return
|
||||||
@ -906,54 +909,54 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
replacement = self._min
|
replacement = self._min
|
||||||
else:
|
else:
|
||||||
replacement = 0
|
replacement = 0
|
||||||
dbg('empty value; setting replacement:', replacement)
|
## dbg('empty value; setting replacement:', replacement)
|
||||||
|
|
||||||
if replacement is None:
|
if replacement is None:
|
||||||
# Go get the integer portion about to be set and verify its validity
|
# Go get the integer portion about to be set and verify its validity
|
||||||
intstart, intend = self._fields[0]._extent
|
intstart, intend = self._fields[0]._extent
|
||||||
dbg('intstart, intend:', intstart, intend)
|
## dbg('intstart, intend:', intstart, intend)
|
||||||
dbg('raw integer:"%s"' % value[intstart:intend])
|
## dbg('raw integer:"%s"' % value[intstart:intend])
|
||||||
int = self._GetNumValue(value[intstart:intend])
|
int = self._GetNumValue(value[intstart:intend])
|
||||||
numval = self._fromGUI(value)
|
numval = self._fromGUI(value)
|
||||||
|
|
||||||
dbg('integer: "%s"' % int)
|
## dbg('integer: "%s"' % int)
|
||||||
try:
|
try:
|
||||||
fracval = self.GetFraction(value)
|
fracval = self.GetFraction(value)
|
||||||
except ValueError, e:
|
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()
|
self._disallowValue()
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return
|
return
|
||||||
|
|
||||||
if fracval == 0.0:
|
if fracval == 0.0:
|
||||||
dbg('self._isNeg?', self._isNeg)
|
## dbg('self._isNeg?', self._isNeg)
|
||||||
if int == '-' and self._oldvalue < 0 and not self._typedSign:
|
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
|
replacement = 0
|
||||||
self._isNeg = False
|
self._isNeg = False
|
||||||
elif int[:2] == '-0' and self._fractionWidth == 0:
|
elif int[:2] == '-0' and self._fractionWidth == 0:
|
||||||
if self._oldvalue < 0:
|
if self._oldvalue < 0:
|
||||||
dbg('-0; setting replacement of 0')
|
## dbg('-0; setting replacement of 0')
|
||||||
replacement = 0
|
replacement = 0
|
||||||
self._isNeg = False
|
self._isNeg = False
|
||||||
elif not self._limited or (self._min < -1 and self._max >= -1):
|
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
|
replacement = -1
|
||||||
self._isNeg = True
|
self._isNeg = True
|
||||||
else:
|
else:
|
||||||
# limited and -1 is out of bounds
|
# limited and -1 is out of bounds
|
||||||
self._disallowValue()
|
self._disallowValue()
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return
|
return
|
||||||
|
|
||||||
elif int == '-' and (self._oldvalue >= 0 or self._typedSign) and self._fractionWidth == 0:
|
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):
|
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
|
replacement = -1
|
||||||
else:
|
else:
|
||||||
# limited and -1 is out of bounds
|
# limited and -1 is out of bounds
|
||||||
self._disallowValue()
|
self._disallowValue()
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return
|
return
|
||||||
|
|
||||||
elif( self._typedSign
|
elif( self._typedSign
|
||||||
@ -963,7 +966,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
# changed sign resulting in value that's now out-of-bounds;
|
# changed sign resulting in value that's now out-of-bounds;
|
||||||
# disallow
|
# disallow
|
||||||
self._disallowValue()
|
self._disallowValue()
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return
|
return
|
||||||
|
|
||||||
if replacement is None:
|
if replacement is None:
|
||||||
@ -974,37 +977,37 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
# integer requested is not legal. This can happen if the user
|
# integer requested is not legal. This can happen if the user
|
||||||
# is attempting to insert a digit in the middle of the control
|
# is attempting to insert a digit in the middle of the control
|
||||||
# resulting in something like " 3 45". Disallow such actions:
|
# 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():
|
if not wx.Validator_IsSilent():
|
||||||
wx.Bell()
|
wx.Bell()
|
||||||
sel_start, sel_to = self._GetSelection()
|
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.SetInsertionPoint, sel_start) # preserve current selection/position
|
||||||
wx.CallAfter(self.SetSelection, sel_start, sel_to)
|
wx.CallAfter(self.SetSelection, sel_start, sel_to)
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return
|
return
|
||||||
|
|
||||||
if int[0] == '0' and len(int) > 1:
|
if int[0] == '0' and len(int) > 1:
|
||||||
dbg('numvalue: "%s"' % numvalue.replace(' ', ''))
|
## dbg('numvalue: "%s"' % numvalue.replace(' ', ''))
|
||||||
if self._fractionWidth:
|
if self._fractionWidth:
|
||||||
value = self._toGUI(string.atof(numvalue))
|
value = self._toGUI(string.atof(numvalue))
|
||||||
else:
|
else:
|
||||||
value = self._toGUI(string.atol(numvalue))
|
value = self._toGUI(string.atol(numvalue))
|
||||||
dbg('modified value: "%s"' % value)
|
## dbg('modified value: "%s"' % value)
|
||||||
|
|
||||||
self._typedSign = False # reset state var
|
self._typedSign = False # reset state var
|
||||||
|
|
||||||
if replacement is not None:
|
if replacement is not None:
|
||||||
# Value presented wasn't a legal number, but control should do something
|
# Value presented wasn't a legal number, but control should do something
|
||||||
# reasonable instead:
|
# reasonable instead:
|
||||||
dbg('setting replacement value:', replacement)
|
## dbg('setting replacement value:', replacement)
|
||||||
self._SetValue(self._toGUI(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)))
|
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.SetInsertionPoint, sel_start)
|
||||||
wx.CallAfter(self.SetSelection, sel_start, sel_to)
|
wx.CallAfter(self.SetSelection, sel_start, sel_to)
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Otherwise, apply appropriate formatting to value:
|
# Otherwise, apply appropriate formatting to value:
|
||||||
@ -1016,35 +1019,35 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
else:
|
else:
|
||||||
self._isNeg = False
|
self._isNeg = False
|
||||||
|
|
||||||
dbg('value:"%s"' % value, 'self._useParens:', self._useParens)
|
## dbg('value:"%s"' % value, 'self._useParens:', self._useParens)
|
||||||
if self._fractionWidth:
|
if self._fractionWidth:
|
||||||
adjvalue = self._adjustFloat(self._GetNumValue(value).replace('.',self._decimalChar))
|
adjvalue = self._adjustFloat(self._GetNumValue(value).replace('.',self._decimalChar))
|
||||||
else:
|
else:
|
||||||
adjvalue = self._adjustInt(self._GetNumValue(value))
|
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
|
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)
|
BaseMaskedTextCtrl._SetValue(self, adjvalue)
|
||||||
# After all actions so far scheduled, check that resulting cursor
|
# After all actions so far scheduled, check that resulting cursor
|
||||||
# position is appropriate, and move if not:
|
# position is appropriate, and move if not:
|
||||||
wx.CallAfter(self._CheckInsertionPoint)
|
wx.CallAfter(self._CheckInsertionPoint)
|
||||||
|
|
||||||
dbg('finished MaskedNumCtrl::_SetValue', indent=0)
|
## dbg('finished NumCtrl::_SetValue', indent=0)
|
||||||
|
|
||||||
def _CheckInsertionPoint(self):
|
def _CheckInsertionPoint(self):
|
||||||
# If current insertion point is before the end of the integer and
|
# If current insertion point is before the end of the integer and
|
||||||
# its before the 1st digit, place it just after the sign position:
|
# 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()
|
sel_start, sel_to = self._GetSelection()
|
||||||
text = self._GetValue()
|
text = self._GetValue()
|
||||||
if sel_to < self._fields[0]._extent[1] and text[sel_to] in (' ', '-', '('):
|
if sel_to < self._fields[0]._extent[1] and text[sel_to] in (' ', '-', '('):
|
||||||
text, signpos, right_signpos = self._getSignedValue()
|
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.SetInsertionPoint(signpos+1)
|
||||||
self.SetSelection(signpos+1, signpos+1)
|
self.SetSelection(signpos+1, signpos+1)
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
|
|
||||||
|
|
||||||
def _OnErase( self, event ):
|
def _OnErase( self, event ):
|
||||||
@ -1053,7 +1056,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
grouping characters auto selects the digit before or after the
|
grouping characters auto selects the digit before or after the
|
||||||
grouping character, so that the erasure does the right thing.
|
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
|
#if grouping digits, make sure deletes next to group char always
|
||||||
# delete next digit to appropriate side:
|
# delete next digit to appropriate side:
|
||||||
@ -1086,21 +1089,21 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
self.SetSelection(sel_start, sel_to+1)
|
self.SetSelection(sel_start, sel_to+1)
|
||||||
|
|
||||||
BaseMaskedTextCtrl._OnErase(self, event)
|
BaseMaskedTextCtrl._OnErase(self, event)
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
|
|
||||||
|
|
||||||
def OnTextChange( self, event ):
|
def OnTextChange( self, event ):
|
||||||
"""
|
"""
|
||||||
Handles an event indicating that the text control's value
|
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
|
NOTE: using wxTextCtrl.SetValue() to change the control's
|
||||||
contents from within a EVT_CHAR handler can cause double
|
contents from within a EVT_CHAR handler can cause double
|
||||||
text events. So we check for actual changes to the text
|
text events. So we check for actual changes to the text
|
||||||
before passing the events on.
|
before passing the events on.
|
||||||
"""
|
"""
|
||||||
dbg('MaskedNumCtrl::OnTextChange', indent=1)
|
## dbg('NumCtrl::OnTextChange', indent=1)
|
||||||
if not BaseMaskedTextCtrl._OnTextChange(self, event):
|
if not BaseMaskedTextCtrl._OnTextChange(self, event):
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return
|
return
|
||||||
|
|
||||||
# else... legal value
|
# else... legal value
|
||||||
@ -1109,14 +1112,14 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
if value != self._oldvalue:
|
if value != self._oldvalue:
|
||||||
try:
|
try:
|
||||||
self.GetEventHandler().ProcessEvent(
|
self.GetEventHandler().ProcessEvent(
|
||||||
MaskedNumNumberUpdatedEvent( self.GetId(), self.GetValue(), self ) )
|
NumberUpdatedEvent( self.GetId(), self.GetValue(), self ) )
|
||||||
except ValueError:
|
except ValueError:
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return
|
return
|
||||||
# let normal processing of the text continue
|
# let normal processing of the text continue
|
||||||
event.Skip()
|
event.Skip()
|
||||||
self._oldvalue = value # record for next event
|
self._oldvalue = value # record for next event
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
|
|
||||||
def _GetValue(self):
|
def _GetValue(self):
|
||||||
"""
|
"""
|
||||||
@ -1172,7 +1175,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
If min > the max value allowed by the width of the control,
|
If min > the max value allowed by the width of the control,
|
||||||
the function will return False, and the min will not be set.
|
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
|
if( self._max is None
|
||||||
or min is None
|
or min is None
|
||||||
or (self._max is not None and self._max >= min) ):
|
or (self._max is not None and self._max >= min) ):
|
||||||
@ -1183,7 +1186,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
bRet = False
|
bRet = False
|
||||||
else:
|
else:
|
||||||
bRet = False
|
bRet = False
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return bRet
|
return bRet
|
||||||
|
|
||||||
def GetMin(self):
|
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
|
also be called with a value to see if that value would fall within
|
||||||
the current bounds of the given control.
|
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:
|
if value is None:
|
||||||
value = self.GetValue()
|
value = self.GetValue()
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
value = self._GetNumValue(self._toGUI(value))
|
value = self._GetNumValue(self._toGUI(value))
|
||||||
except ValueError, e:
|
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
|
return False
|
||||||
if value.strip() == '':
|
if value.strip() == '':
|
||||||
value = None
|
value = None
|
||||||
@ -1309,10 +1312,10 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
|
|
||||||
# if bounds set, and value is None, return False
|
# if bounds set, and value is None, return False
|
||||||
if value == None and (min is not None or max is not None):
|
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
|
return 0
|
||||||
else:
|
else:
|
||||||
dbg('finished IsInBounds', indent=0)
|
## dbg('finished IsInBounds', indent=0)
|
||||||
return min <= value <= max
|
return min <= value <= max
|
||||||
|
|
||||||
|
|
||||||
@ -1383,21 +1386,21 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
type and bounds checking and raises ValueError if argument is
|
type and bounds checking and raises ValueError if argument is
|
||||||
not a valid value.
|
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():
|
if value is None and self.IsNoneAllowed():
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return self._template
|
return self._template
|
||||||
|
|
||||||
elif type(value) in (types.StringType, types.UnicodeType):
|
elif type(value) in (types.StringType, types.UnicodeType):
|
||||||
value = self._GetNumValue(value)
|
value = self._GetNumValue(value)
|
||||||
dbg('cleansed num value: "%s"' % value)
|
## dbg('cleansed num value: "%s"' % value)
|
||||||
if value == "":
|
if value == "":
|
||||||
if self.IsNoneAllowed():
|
if self.IsNoneAllowed():
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return self._template
|
return self._template
|
||||||
else:
|
else:
|
||||||
dbg('exception raised:', e, indent=0)
|
## dbg('exception raised:', e, indent=0)
|
||||||
raise ValueError ('wxMaskedNumCtrl requires numeric value, passed %s'% repr(value) )
|
raise ValueError ('NumCtrl requires numeric value, passed %s'% repr(value) )
|
||||||
# else...
|
# else...
|
||||||
try:
|
try:
|
||||||
if self._fractionWidth or value.find('.') != -1:
|
if self._fractionWidth or value.find('.') != -1:
|
||||||
@ -1405,13 +1408,13 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
else:
|
else:
|
||||||
value = long(value)
|
value = long(value)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
dbg('exception raised:', e, indent=0)
|
## dbg('exception raised:', e, indent=0)
|
||||||
raise ValueError ('MaskedNumCtrl requires numeric value, passed %s'% repr(value) )
|
raise ValueError ('NumCtrl requires numeric value, passed %s'% repr(value) )
|
||||||
|
|
||||||
elif type(value) not in (types.IntType, types.LongType, types.FloatType):
|
elif type(value) not in (types.IntType, types.LongType, types.FloatType):
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
raise ValueError (
|
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:
|
if not self._allowNegative and value < 0:
|
||||||
raise ValueError (
|
raise ValueError (
|
||||||
@ -1421,29 +1424,29 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
min = self.GetMin()
|
min = self.GetMin()
|
||||||
max = self.GetMax()
|
max = self.GetMax()
|
||||||
if not min is None and value < min:
|
if not min is None and value < min:
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
raise ValueError (
|
raise ValueError (
|
||||||
'value %d is below minimum value of control'% value )
|
'value %d is below minimum value of control'% value )
|
||||||
if not max is None and value > max:
|
if not max is None and value > max:
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
raise ValueError (
|
raise ValueError (
|
||||||
'value %d exceeds value of control'% value )
|
'value %d exceeds value of control'% value )
|
||||||
|
|
||||||
adjustwidth = len(self._mask) - (1 * self._useParens * self._signOk)
|
adjustwidth = len(self._mask) - (1 * self._useParens * self._signOk)
|
||||||
dbg('len(%s):' % self._mask, len(self._mask))
|
## dbg('len(%s):' % self._mask, len(self._mask))
|
||||||
dbg('adjustwidth - groupSpace:', adjustwidth - self._groupSpace)
|
## dbg('adjustwidth - groupSpace:', adjustwidth - self._groupSpace)
|
||||||
dbg('adjustwidth:', adjustwidth)
|
## dbg('adjustwidth:', adjustwidth)
|
||||||
if self._fractionWidth == 0:
|
if self._fractionWidth == 0:
|
||||||
s = str(long(value)).rjust(self._integerWidth)
|
s = str(long(value)).rjust(self._integerWidth)
|
||||||
else:
|
else:
|
||||||
format = '%' + '%d.%df' % (self._integerWidth+self._fractionWidth+1, self._fractionWidth)
|
format = '%' + '%d.%df' % (self._integerWidth+self._fractionWidth+1, self._fractionWidth)
|
||||||
s = format % float(value)
|
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):
|
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))
|
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):
|
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))
|
raise ValueError ('value %s exceeds the integer width of the control (%d)' % (s, self._integerWidth))
|
||||||
|
|
||||||
s = s.rjust(adjustwidth).replace('.', self._decimalChar)
|
s = s.rjust(adjustwidth).replace('.', self._decimalChar)
|
||||||
@ -1452,7 +1455,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
s = s.replace('-', '(') + ')'
|
s = s.replace('-', '(') + ')'
|
||||||
else:
|
else:
|
||||||
s += ' '
|
s += ' '
|
||||||
dbg('returned: "%s"' % s, indent=0)
|
## dbg('returned: "%s"' % s, indent=0)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
@ -1460,8 +1463,8 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
"""
|
"""
|
||||||
Conversion function used in getting the value of the control.
|
Conversion function used in getting the value of the control.
|
||||||
"""
|
"""
|
||||||
dbg(suspend=0)
|
## dbg(suspend=0)
|
||||||
dbg('MaskedNumCtrl::_fromGUI(%s)' % value, indent=1)
|
## dbg('NumCtrl::_fromGUI(%s)' % value, indent=1)
|
||||||
# One or more of the underlying text control implementations
|
# One or more of the underlying text control implementations
|
||||||
# issue an intermediate EVT_TEXT when replacing the control's
|
# issue an intermediate EVT_TEXT when replacing the control's
|
||||||
# value, where the intermediate value is an empty string.
|
# value, where the intermediate value is an empty string.
|
||||||
@ -1470,42 +1473,42 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
#
|
#
|
||||||
if value.strip() == '':
|
if value.strip() == '':
|
||||||
if not self.IsNoneAllowed():
|
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:
|
if self._fractionWidth:
|
||||||
return 0.0
|
return 0.0
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
dbg('empty value; returning None', indent = 0)
|
## dbg('empty value; returning None', indent = 0)
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
value = self._GetNumValue(value)
|
value = self._GetNumValue(value)
|
||||||
dbg('Num value: "%s"' % value)
|
## dbg('Num value: "%s"' % value)
|
||||||
if self._fractionWidth:
|
if self._fractionWidth:
|
||||||
try:
|
try:
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return float( value )
|
return float( value )
|
||||||
except ValueError:
|
except ValueError:
|
||||||
dbg("couldn't convert to float; returning None")
|
## dbg("couldn't convert to float; returning None")
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return int( value )
|
return int( value )
|
||||||
except ValueError:
|
except ValueError:
|
||||||
try:
|
try:
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return long( value )
|
return long( value )
|
||||||
except ValueError:
|
except ValueError:
|
||||||
dbg("couldn't convert to long; returning None")
|
## dbg("couldn't convert to long; returning None")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
dbg('exception occurred; returning None')
|
## dbg('exception occurred; returning None')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@ -1514,7 +1517,7 @@ class MaskedNumCtrl(BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin):
|
|||||||
Preprocessor for base control paste; if value needs to be right-justified
|
Preprocessor for base control paste; if value needs to be right-justified
|
||||||
to fit in control, do so prior to paste:
|
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:
|
if value is None:
|
||||||
paste_text = self._getClipboardContents()
|
paste_text = self._getClipboardContents()
|
||||||
else:
|
else:
|
||||||
@ -1545,7 +1548,7 @@ if __name__ == '__main__':
|
|||||||
style = wx.DEFAULT_DIALOG_STYLE ):
|
style = wx.DEFAULT_DIALOG_STYLE ):
|
||||||
wx.Dialog.__init__(self, parent, id, title, pos, size, 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.OK = wx.Button( self, wx.ID_OK, "OK")
|
||||||
self.Cancel = wx.Button( self, wx.ID_CANCEL, "Cancel")
|
self.Cancel = wx.Button( self, wx.ID_CANCEL, "Cancel")
|
||||||
|
|
||||||
@ -1560,7 +1563,7 @@ if __name__ == '__main__':
|
|||||||
self.SetSizer( vs )
|
self.SetSizer( vs )
|
||||||
vs.Fit( self )
|
vs.Fit( self )
|
||||||
vs.SetSizeHints( 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):
|
def OnChange(self, event):
|
||||||
print 'value now', event.GetValue()
|
print 'value now', event.GetValue()
|
||||||
@ -1578,7 +1581,7 @@ if __name__ == '__main__':
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def OnClick(self, event):
|
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.SetValue(501)
|
||||||
dlg.int_ctrl.SetInsertionPoint(1)
|
dlg.int_ctrl.SetInsertionPoint(1)
|
||||||
dlg.int_ctrl.SetSelection(1,2)
|
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
|
||||||
|
|
||||||
|
|
@ -39,8 +39,8 @@
|
|||||||
#
|
#
|
||||||
# 12/20/2003 - Jeff Grimmett (grimmtooth@softhome.net)
|
# 12/20/2003 - Jeff Grimmett (grimmtooth@softhome.net)
|
||||||
#
|
#
|
||||||
# o wxMaskedTextCtrl -> MaskedTextCtrl
|
# o wxMaskedTextCtrl -> masked.TextCtrl
|
||||||
# o wxTimeCtrl -> TimeCtrl
|
# o wxTimeCtrl -> masked.TimeCtrl
|
||||||
#
|
#
|
||||||
|
|
||||||
"""<html><body>
|
"""<html><body>
|
||||||
@ -272,10 +272,10 @@ import types
|
|||||||
import wx
|
import wx
|
||||||
|
|
||||||
from wx.tools.dbg import Logger
|
from wx.tools.dbg import Logger
|
||||||
from wx.lib.maskededit import BaseMaskedTextCtrl, Field
|
from wx.lib.masked import Field, BaseMaskedTextCtrl
|
||||||
|
|
||||||
dbg = Logger()
|
dbg = Logger()
|
||||||
dbg(enable=0)
|
##dbg(enable=0)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from mx import DateTime
|
from mx import DateTime
|
||||||
@ -333,7 +333,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
'max': None,
|
'max': None,
|
||||||
'limited': False, # by default, no limiting even if bounds set
|
'limited': False, # by default, no limiting even if bounds set
|
||||||
'useFixedWidthFont': True, # by default, use a fixed-width font
|
'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__ (
|
def __init__ (
|
||||||
@ -347,7 +347,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
**kwargs ):
|
**kwargs ):
|
||||||
|
|
||||||
# set defaults for control:
|
# set defaults for control:
|
||||||
dbg('setting defaults:')
|
## dbg('setting defaults:')
|
||||||
for key, param_value in TimeCtrl.valid_ctrl_params.items():
|
for key, param_value in TimeCtrl.valid_ctrl_params.items():
|
||||||
# This is done this way to make setattr behave consistently with
|
# This is done this way to make setattr behave consistently with
|
||||||
# "private attribute" name mangling
|
# "private attribute" name mangling
|
||||||
@ -456,7 +456,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
|
|
||||||
|
|
||||||
def SetParameters(self, **kwargs):
|
def SetParameters(self, **kwargs):
|
||||||
dbg('TimeCtrl::SetParameters(%s)' % repr(kwargs), indent=1)
|
## dbg('TimeCtrl::SetParameters(%s)' % repr(kwargs), indent=1)
|
||||||
maskededit_kwargs = {}
|
maskededit_kwargs = {}
|
||||||
reset_format = False
|
reset_format = False
|
||||||
|
|
||||||
@ -553,10 +553,10 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
self.SetValue(value)
|
self.SetValue(value)
|
||||||
except:
|
except:
|
||||||
self.SetValue('12:00:00 AM')
|
self.SetValue('12:00:00 AM')
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return {} # no arguments to return
|
return {} # no arguments to return
|
||||||
else:
|
else:
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return maskededit_kwargs
|
return maskededit_kwargs
|
||||||
|
|
||||||
|
|
||||||
@ -565,7 +565,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
This function binds an externally created spin button to the control, so that
|
This function binds an externally created spin button to the control, so that
|
||||||
up/down events from the button automatically change the control.
|
up/down events from the button automatically change the control.
|
||||||
"""
|
"""
|
||||||
dbg('TimeCtrl::BindSpinButton')
|
## dbg('TimeCtrl::BindSpinButton')
|
||||||
self.__spinButton = sb
|
self.__spinButton = sb
|
||||||
if self.__spinButton:
|
if self.__spinButton:
|
||||||
# bind event handlers to spin ctrl
|
# bind event handlers to spin ctrl
|
||||||
@ -584,16 +584,16 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
and convert wxDateTime, mxDateTime, or 12/24 format time string
|
and convert wxDateTime, mxDateTime, or 12/24 format time string
|
||||||
into the appropriate format string for the control.
|
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:
|
try:
|
||||||
strtime = self._toGUI(self.__validateValue(value))
|
strtime = self._toGUI(self.__validateValue(value))
|
||||||
except:
|
except:
|
||||||
dbg('validation failed', indent=0)
|
## dbg('validation failed', indent=0)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
dbg('strtime:', strtime)
|
## dbg('strtime:', strtime)
|
||||||
self._SetValue(strtime)
|
self._SetValue(strtime)
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
|
|
||||||
def GetValue(self,
|
def GetValue(self,
|
||||||
as_wxDateTime = False,
|
as_wxDateTime = False,
|
||||||
@ -641,12 +641,12 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
raised.
|
raised.
|
||||||
"""
|
"""
|
||||||
global accept_mx
|
global accept_mx
|
||||||
dbg(suspend=1)
|
## dbg(suspend=1)
|
||||||
dbg('TimeCtrl::GetWxDateTime(%s)' % repr(value), indent=1)
|
## dbg('TimeCtrl::GetWxDateTime(%s)' % repr(value), indent=1)
|
||||||
if value is None:
|
if value is None:
|
||||||
dbg('getting control value')
|
## dbg('getting control value')
|
||||||
value = self.GetValue()
|
value = self.GetValue()
|
||||||
dbg('value = "%s"' % value)
|
## dbg('value = "%s"' % value)
|
||||||
|
|
||||||
if type(value) == types.UnicodeType:
|
if type(value) == types.UnicodeType:
|
||||||
value = str(value) # convert to regular string
|
value = str(value) # convert to regular string
|
||||||
@ -656,14 +656,14 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
|
|
||||||
# Construct constant wxDateTime, then try to parse the string:
|
# Construct constant wxDateTime, then try to parse the string:
|
||||||
wxdt = wx.DateTimeFromDMY(1, 0, 1970)
|
wxdt = wx.DateTimeFromDMY(1, 0, 1970)
|
||||||
dbg('attempting conversion')
|
## dbg('attempting conversion')
|
||||||
value = value.strip() # (parser doesn't like leading spaces)
|
value = value.strip() # (parser doesn't like leading spaces)
|
||||||
checkTime = wxdt.ParseTime(value)
|
checkTime = wxdt.ParseTime(value)
|
||||||
valid = checkTime == len(value) # entire string parsed?
|
valid = checkTime == len(value) # entire string parsed?
|
||||||
dbg('checkTime == len(value)?', valid)
|
## dbg('checkTime == len(value)?', valid)
|
||||||
|
|
||||||
if not valid:
|
if not valid:
|
||||||
dbg(indent=0, suspend=0)
|
## dbg(indent=0, suspend=0)
|
||||||
raise ValueError('cannot convert string "%s" to valid time' % value)
|
raise ValueError('cannot convert string "%s" to valid time' % value)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -685,7 +685,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
error = 'GetWxDateTime requires wxDateTime, mxDateTime or parsable time string, passed %s'% repr(value)
|
error = 'GetWxDateTime requires wxDateTime, mxDateTime or parsable time string, passed %s'% repr(value)
|
||||||
else:
|
else:
|
||||||
error = 'GetWxDateTime requires wxDateTime or parsable time string, passed %s'% repr(value)
|
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)
|
raise ValueError(error)
|
||||||
|
|
||||||
wxdt = wx.DateTimeFromDMY(1, 0, 1970)
|
wxdt = wx.DateTimeFromDMY(1, 0, 1970)
|
||||||
@ -693,7 +693,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
wxdt.SetMinute(minute)
|
wxdt.SetMinute(minute)
|
||||||
wxdt.SetSecond(second)
|
wxdt.SetSecond(second)
|
||||||
|
|
||||||
dbg('wxdt:', wxdt, indent=0, suspend=0)
|
## dbg('wxdt:', wxdt, indent=0, suspend=0)
|
||||||
return wxdt
|
return wxdt
|
||||||
|
|
||||||
|
|
||||||
@ -730,13 +730,13 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
adjusted to the new minimum value; if not limited, the value in the
|
adjusted to the new minimum value; if not limited, the value in the
|
||||||
control will be colored as invalid.
|
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:
|
if min is not None:
|
||||||
try:
|
try:
|
||||||
min = self.GetWxDateTime(min)
|
min = self.GetWxDateTime(min)
|
||||||
self.__min = self._toGUI(min)
|
self.__min = self._toGUI(min)
|
||||||
except:
|
except:
|
||||||
dbg('exception occurred', indent=0)
|
## dbg('exception occurred', indent=0)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
self.__min = min
|
self.__min = min
|
||||||
@ -746,7 +746,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
else:
|
else:
|
||||||
self._CheckValid()
|
self._CheckValid()
|
||||||
ret = True
|
ret = True
|
||||||
dbg('ret:', ret, indent=0)
|
## dbg('ret:', ret, indent=0)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@ -757,22 +757,23 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
the current minimum bound on the control, as a wxDateTime
|
the current minimum bound on the control, as a wxDateTime
|
||||||
by default, or as a string if as_string argument is True.
|
by default, or as a string if as_string argument is True.
|
||||||
"""
|
"""
|
||||||
dbg(suspend=1)
|
## dbg(suspend=1)
|
||||||
dbg('TimeCtrl::GetMin, as_string?', as_string, indent=1)
|
## dbg('TimeCtrl::GetMin, as_string?', as_string, indent=1)
|
||||||
if self.__min is None:
|
if self.__min is None:
|
||||||
dbg('(min == None)')
|
## dbg('(min == None)')
|
||||||
ret = self.__min
|
ret = self.__min
|
||||||
elif as_string:
|
elif as_string:
|
||||||
ret = self.__min
|
ret = self.__min
|
||||||
dbg('ret:', ret)
|
## dbg('ret:', ret)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
ret = self.GetWxDateTime(self.__min)
|
ret = self.GetWxDateTime(self.__min)
|
||||||
except:
|
except:
|
||||||
dbg(suspend=0)
|
## dbg(suspend=0)
|
||||||
dbg('exception occurred', indent=0)
|
## dbg('exception occurred', indent=0)
|
||||||
dbg('ret:', repr(ret))
|
raise
|
||||||
dbg(indent=0, suspend=0)
|
## dbg('ret:', repr(ret))
|
||||||
|
## dbg(indent=0, suspend=0)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@ -789,23 +790,23 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
adjusted to this maximum value; if not limited, the value in the
|
adjusted to this maximum value; if not limited, the value in the
|
||||||
control will be colored as invalid.
|
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:
|
if max is not None:
|
||||||
try:
|
try:
|
||||||
max = self.GetWxDateTime(max)
|
max = self.GetWxDateTime(max)
|
||||||
self.__max = self._toGUI(max)
|
self.__max = self._toGUI(max)
|
||||||
except:
|
except:
|
||||||
dbg('exception occurred', indent=0)
|
## dbg('exception occurred', indent=0)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
self.__max = max
|
self.__max = max
|
||||||
dbg('max:', repr(self.__max))
|
## dbg('max:', repr(self.__max))
|
||||||
if self.IsLimited() and not self.IsInBounds():
|
if self.IsLimited() and not self.IsInBounds():
|
||||||
self.SetLimited(self.__limited) # force limited value:
|
self.SetLimited(self.__limited) # force limited value:
|
||||||
else:
|
else:
|
||||||
self._CheckValid()
|
self._CheckValid()
|
||||||
ret = True
|
ret = True
|
||||||
dbg('ret:', ret, indent=0)
|
## dbg('ret:', ret, indent=0)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@ -816,23 +817,23 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
the current minimum bound on the control, as a wxDateTime
|
the current minimum bound on the control, as a wxDateTime
|
||||||
by default, or as a string if as_string argument is True.
|
by default, or as a string if as_string argument is True.
|
||||||
"""
|
"""
|
||||||
dbg(suspend=1)
|
## dbg(suspend=1)
|
||||||
dbg('TimeCtrl::GetMin, as_string?', as_string, indent=1)
|
## dbg('TimeCtrl::GetMin, as_string?', as_string, indent=1)
|
||||||
if self.__max is None:
|
if self.__max is None:
|
||||||
dbg('(max == None)')
|
## dbg('(max == None)')
|
||||||
ret = self.__max
|
ret = self.__max
|
||||||
elif as_string:
|
elif as_string:
|
||||||
ret = self.__max
|
ret = self.__max
|
||||||
dbg('ret:', ret)
|
## dbg('ret:', ret)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
ret = self.GetWxDateTime(self.__max)
|
ret = self.GetWxDateTime(self.__max)
|
||||||
except:
|
except:
|
||||||
dbg(suspend=0)
|
## dbg(suspend=0)
|
||||||
dbg('exception occurred', indent=0)
|
## dbg('exception occurred', indent=0)
|
||||||
raise
|
raise
|
||||||
dbg('ret:', repr(ret))
|
## dbg('ret:', repr(ret))
|
||||||
dbg(indent=0, suspend=0)
|
## dbg(indent=0, suspend=0)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@ -868,22 +869,22 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
limiting, but coloring of out-of-bounds values will still take
|
limiting, but coloring of out-of-bounds values will still take
|
||||||
place if bounds have been set for the control.
|
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
|
self.__limited = limited
|
||||||
|
|
||||||
if not limited:
|
if not limited:
|
||||||
self.SetMaskParameters(validRequired = False)
|
self.SetMaskParameters(validRequired = False)
|
||||||
self._CheckValid()
|
self._CheckValid()
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return
|
return
|
||||||
|
|
||||||
dbg('requiring valid value')
|
## dbg('requiring valid value')
|
||||||
self.SetMaskParameters(validRequired = True)
|
self.SetMaskParameters(validRequired = True)
|
||||||
|
|
||||||
min = self.GetMin()
|
min = self.GetMin()
|
||||||
max = self.GetMax()
|
max = self.GetMax()
|
||||||
if min is None or max is None:
|
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
|
return # can't limit without 2 bounds
|
||||||
|
|
||||||
elif not self.IsInBounds():
|
elif not self.IsInBounds():
|
||||||
@ -891,11 +892,11 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
try:
|
try:
|
||||||
value = self.GetWxDateTime()
|
value = self.GetWxDateTime()
|
||||||
except:
|
except:
|
||||||
dbg('exception occurred', indent=0)
|
## dbg('exception occurred', indent=0)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if min <= max: # valid range doesn't span midnight
|
if min <= max: # valid range doesn't span midnight
|
||||||
dbg('min <= max')
|
## dbg('min <= max')
|
||||||
# which makes the "nearest bound" computation trickier...
|
# which makes the "nearest bound" computation trickier...
|
||||||
|
|
||||||
# determine how long the "invalid" pie wedge is, and cut
|
# determine how long the "invalid" pie wedge is, and cut
|
||||||
@ -918,24 +919,24 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
cmp_value = value
|
cmp_value = value
|
||||||
|
|
||||||
if (cmp_value - max) > half_interval:
|
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)
|
self.SetValue(min)
|
||||||
else:
|
else:
|
||||||
dbg('forcing value to max (%s)' % max.FormatTime())
|
## dbg('forcing value to max (%s)' % max.FormatTime())
|
||||||
self.SetValue(max)
|
self.SetValue(max)
|
||||||
else:
|
else:
|
||||||
dbg('max < min')
|
## dbg('max < min')
|
||||||
# therefore max < value < min guaranteed to be true,
|
# therefore max < value < min guaranteed to be true,
|
||||||
# so "nearest bound" calculation is much easier:
|
# so "nearest bound" calculation is much easier:
|
||||||
if (value - max) >= (min - value):
|
if (value - max) >= (min - value):
|
||||||
# current value closer to min; pick that edge of pie wedge
|
# 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)
|
self.SetValue(min)
|
||||||
else:
|
else:
|
||||||
dbg('forcing value to max (%s)' % max.FormatTime())
|
## dbg('forcing value to max (%s)' % max.FormatTime())
|
||||||
self.SetValue(max)
|
self.SetValue(max)
|
||||||
|
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -961,21 +962,22 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
try:
|
try:
|
||||||
value = self.GetWxDateTime(value) # try to regularize passed value
|
value = self.GetWxDateTime(value) # try to regularize passed value
|
||||||
except ValueError:
|
except ValueError:
|
||||||
dbg('ValueError getting wxDateTime for %s' % repr(value), indent=0)
|
## dbg('ValueError getting wxDateTime for %s' % repr(value), indent=0)
|
||||||
raise
|
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:
|
if self.__min is None or self.__max is None:
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
elif value is None:
|
elif value is None:
|
||||||
try:
|
try:
|
||||||
value = self.GetWxDateTime()
|
value = self.GetWxDateTime()
|
||||||
except:
|
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:
|
# Get wxDateTime representations of bounds:
|
||||||
min = self.GetMin()
|
min = self.GetMin()
|
||||||
@ -990,7 +992,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
# either "min" <= value (<= midnight of *next day*)
|
# either "min" <= value (<= midnight of *next day*)
|
||||||
# or midnight <= value <= "max"
|
# or midnight <= value <= "max"
|
||||||
ret = min <= value 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
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@ -1021,7 +1023,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
|
|
||||||
|
|
||||||
def __OnTextChange(self, event=None):
|
def __OnTextChange(self, event=None):
|
||||||
dbg('TimeCtrl::OnTextChange', indent=1)
|
## dbg('TimeCtrl::OnTextChange', indent=1)
|
||||||
|
|
||||||
# Allow Maskedtext base control to color as appropriate,
|
# Allow Maskedtext base control to color as appropriate,
|
||||||
# and Skip the EVT_TEXT event (if appropriate.)
|
# and Skip the EVT_TEXT event (if appropriate.)
|
||||||
@ -1035,11 +1037,11 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
if not BaseMaskedTextCtrl._OnTextChange(self, event):
|
if not BaseMaskedTextCtrl._OnTextChange(self, event):
|
||||||
return
|
return
|
||||||
|
|
||||||
dbg('firing TimeUpdatedEvent...')
|
## dbg('firing TimeUpdatedEvent...')
|
||||||
evt = TimeUpdatedEvent(self.GetId(), self.GetValue())
|
evt = TimeUpdatedEvent(self.GetId(), self.GetValue())
|
||||||
evt.SetEventObject(self)
|
evt.SetEventObject(self)
|
||||||
self.GetEventHandler().ProcessEvent(evt)
|
self.GetEventHandler().ProcessEvent(evt)
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
|
|
||||||
|
|
||||||
def SetInsertionPoint(self, pos):
|
def SetInsertionPoint(self, pos):
|
||||||
@ -1048,14 +1050,14 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
This is necessary to handle the optional spin button, because the insertion
|
This is necessary to handle the optional spin button, because the insertion
|
||||||
point is lost when the focus shifts to the spin button.
|
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)
|
BaseMaskedTextCtrl.SetInsertionPoint(self, pos) # (causes EVT_TEXT event to fire)
|
||||||
self.__posCurrent = self.GetInsertionPoint()
|
self.__posCurrent = self.GetInsertionPoint()
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
|
|
||||||
|
|
||||||
def SetSelection(self, sel_start, sel_to):
|
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
|
# Adjust selection range to legal extent if not already
|
||||||
if sel_start < 0:
|
if sel_start < 0:
|
||||||
@ -1069,7 +1071,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
|
|
||||||
self.__bSelection = sel_start != sel_to
|
self.__bSelection = sel_start != sel_to
|
||||||
BaseMaskedTextCtrl.SetSelection(self, sel_start, sel_to)
|
BaseMaskedTextCtrl.SetSelection(self, sel_start, sel_to)
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
|
|
||||||
|
|
||||||
def __OnSpin(self, key):
|
def __OnSpin(self, key):
|
||||||
@ -1085,7 +1087,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
start, end = self._FindField(self.__posCurrent)._extent
|
start, end = self._FindField(self.__posCurrent)._extent
|
||||||
self.SetInsertionPoint(start)
|
self.SetInsertionPoint(start)
|
||||||
self.SetSelection(start, end)
|
self.SetSelection(start, end)
|
||||||
dbg('current position:', self.__posCurrent)
|
## dbg('current position:', self.__posCurrent)
|
||||||
|
|
||||||
|
|
||||||
def __OnSpinUp(self, event):
|
def __OnSpinUp(self, event):
|
||||||
@ -1093,10 +1095,10 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
Event handler for any bound spin button on EVT_SPIN_UP;
|
Event handler for any bound spin button on EVT_SPIN_UP;
|
||||||
causes control to behave as if up arrow was pressed.
|
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)
|
self.__OnSpin(wx.WXK_UP)
|
||||||
keep_processing = False
|
keep_processing = False
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return keep_processing
|
return keep_processing
|
||||||
|
|
||||||
|
|
||||||
@ -1105,10 +1107,10 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
Event handler for any bound spin button on EVT_SPIN_DOWN;
|
Event handler for any bound spin button on EVT_SPIN_DOWN;
|
||||||
causes control to behave as if down arrow was pressed.
|
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)
|
self.__OnSpin(wx.WXK_DOWN)
|
||||||
keep_processing = False
|
keep_processing = False
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return keep_processing
|
return keep_processing
|
||||||
|
|
||||||
|
|
||||||
@ -1119,14 +1121,14 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
It then calls the base control's _OnChar routine with the modified
|
It then calls the base control's _OnChar routine with the modified
|
||||||
event instance.
|
event instance.
|
||||||
"""
|
"""
|
||||||
dbg('TimeCtrl::OnChar', indent=1)
|
## dbg('TimeCtrl::OnChar', indent=1)
|
||||||
keycode = event.GetKeyCode()
|
keycode = event.GetKeyCode()
|
||||||
dbg('keycode:', keycode)
|
## dbg('keycode:', keycode)
|
||||||
if keycode == ord(':'):
|
if keycode == ord(':'):
|
||||||
dbg('colon seen! removing shift attribute')
|
## dbg('colon seen! removing shift attribute')
|
||||||
event.m_shiftDown = False
|
event.m_shiftDown = False
|
||||||
BaseMaskedTextCtrl._OnChar(self, event ) ## handle each keypress
|
BaseMaskedTextCtrl._OnChar(self, event ) ## handle each keypress
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
|
|
||||||
|
|
||||||
def __OnSetToNow(self, event):
|
def __OnSetToNow(self, event):
|
||||||
@ -1144,7 +1146,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
Event handler for motion events; this handler
|
Event handler for motion events; this handler
|
||||||
changes limits the selection to the new cell boundaries.
|
changes limits the selection to the new cell boundaries.
|
||||||
"""
|
"""
|
||||||
dbg('TimeCtrl::LimitSelection', indent=1)
|
## dbg('TimeCtrl::LimitSelection', indent=1)
|
||||||
pos = self.GetInsertionPoint()
|
pos = self.GetInsertionPoint()
|
||||||
self.__posCurrent = pos
|
self.__posCurrent = pos
|
||||||
sel_start, sel_to = self.GetSelection()
|
sel_start, sel_to = self.GetSelection()
|
||||||
@ -1155,18 +1157,18 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
if sel_to < pos: sel_to = start
|
if sel_to < pos: sel_to = start
|
||||||
elif sel_to > pos: sel_to = end
|
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.SetInsertionPoint(self.__posCurrent)
|
||||||
self.SetSelection(self.__posCurrent, sel_to)
|
self.SetSelection(self.__posCurrent, sel_to)
|
||||||
if event: event.Skip()
|
if event: event.Skip()
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
|
|
||||||
|
|
||||||
def __IncrementValue(self, key, pos):
|
def __IncrementValue(self, key, pos):
|
||||||
dbg('TimeCtrl::IncrementValue', key, pos, indent=1)
|
## dbg('TimeCtrl::IncrementValue', key, pos, indent=1)
|
||||||
text = self.GetValue()
|
text = self.GetValue()
|
||||||
field = self._FindField(pos)
|
field = self._FindField(pos)
|
||||||
dbg('field: ', field._index)
|
## dbg('field: ', field._index)
|
||||||
start, end = field._extent
|
start, end = field._extent
|
||||||
slice = text[start:end]
|
slice = text[start:end]
|
||||||
if key == wx.WXK_UP: increment = 1
|
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:
|
# am/pm setting. So, we use wxDateTime to generate a new value for us:
|
||||||
# (Use a fixed date not subject to DST variations:)
|
# (Use a fixed date not subject to DST variations:)
|
||||||
converter = wx.DateTimeFromDMY(1, 0, 1970)
|
converter = wx.DateTimeFromDMY(1, 0, 1970)
|
||||||
dbg('text: "%s"' % text)
|
## dbg('text: "%s"' % text)
|
||||||
converter.ParseTime(text.strip())
|
converter.ParseTime(text.strip())
|
||||||
currenthour = converter.GetHour()
|
currenthour = converter.GetHour()
|
||||||
dbg('current hour:', currenthour)
|
## dbg('current hour:', currenthour)
|
||||||
newhour = (currenthour + increment) % 24
|
newhour = (currenthour + increment) % 24
|
||||||
dbg('newhour:', newhour)
|
## dbg('newhour:', newhour)
|
||||||
converter.SetHour(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()
|
newvalue = converter # take advantage of auto-conversion for am/pm in .SetValue()
|
||||||
|
|
||||||
else: # minute or second field; handled the same way:
|
else: # minute or second field; handled the same way:
|
||||||
@ -1202,7 +1204,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
except ValueError: # must not be in bounds:
|
except ValueError: # must not be in bounds:
|
||||||
if not wx.Validator_IsSilent():
|
if not wx.Validator_IsSilent():
|
||||||
wx.Bell()
|
wx.Bell()
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
|
|
||||||
|
|
||||||
def _toGUI( self, wxdt ):
|
def _toGUI( self, wxdt ):
|
||||||
@ -1227,23 +1229,23 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
|||||||
not a valid value for the control as currently specified.
|
not a valid value for the control as currently specified.
|
||||||
It is used by both the SetValue() and the IsValid() methods.
|
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:
|
if not value:
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
raise ValueError('%s not a valid time value' % repr(value))
|
raise ValueError('%s not a valid time value' % repr(value))
|
||||||
|
|
||||||
valid = True # assume true
|
valid = True # assume true
|
||||||
try:
|
try:
|
||||||
value = self.GetWxDateTime(value) # regularize form; can generate ValueError if problem doing so
|
value = self.GetWxDateTime(value) # regularize form; can generate ValueError if problem doing so
|
||||||
except:
|
except:
|
||||||
dbg('exception occurred', indent=0)
|
## dbg('exception occurred', indent=0)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if self.IsLimited() and not self.IsInBounds(value):
|
if self.IsLimited() and not self.IsInBounds(value):
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
raise ValueError (
|
raise ValueError (
|
||||||
'value %s is not within the bounds of the control' % str(value) )
|
'value %s is not within the bounds of the control' % str(value) )
|
||||||
dbg(indent=0)
|
## dbg(indent=0)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
@ -1278,12 +1280,12 @@ if __name__ == '__main__':
|
|||||||
self.Bind(EVT_TIMEUPDATE, self.OnTimeChange, self.tc)
|
self.Bind(EVT_TIMEUPDATE, self.OnTimeChange, self.tc)
|
||||||
|
|
||||||
def OnTimeChange(self, event):
|
def OnTimeChange(self, event):
|
||||||
dbg('OnTimeChange: value = ', event.GetValue())
|
## dbg('OnTimeChange: value = ', event.GetValue())
|
||||||
wxdt = self.tc.GetWxDateTime()
|
wxdt = self.tc.GetWxDateTime()
|
||||||
dbg('wxdt =', wxdt.GetHour(), wxdt.GetMinute(), wxdt.GetSecond())
|
## dbg('wxdt =', wxdt.GetHour(), wxdt.GetMinute(), wxdt.GetSecond())
|
||||||
if self.test_mx:
|
if self.test_mx:
|
||||||
mxdt = self.tc.GetMxDateTime()
|
mxdt = self.tc.GetMxDateTime()
|
||||||
dbg('mxdt =', mxdt.hour, mxdt.minute, mxdt.second)
|
## dbg('mxdt =', mxdt.hour, mxdt.minute, mxdt.second)
|
||||||
|
|
||||||
|
|
||||||
class MyApp(wx.App):
|
class MyApp(wx.App):
|
@ -2,9 +2,21 @@
|
|||||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
## 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.
|
## 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
|
MASKEDTEXT = wx.lib.masked.ctrl.TEXT
|
||||||
wxMaskedCtrl = wx.lib.maskedctrl.MaskedCtrl
|
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
|
## 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
|
## 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.
|
## 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
|
wxMaskedEditMixin = wx.lib.masked.MaskedEditMixin
|
||||||
test = wx.lib.maskededit.test
|
wxMaskedTextCtrl = wx.lib.masked.TextCtrl
|
||||||
test2 = wx.lib.maskededit.test2
|
wxMaskedComboBox = wx.lib.masked.ComboBox
|
||||||
wxIpAddrCtrl = wx.lib.maskededit.IpAddrCtrl
|
wxMaskedComboBoxSelectEvent = wx.lib.masked.MaskedComboBoxSelectEvent
|
||||||
wxMaskedComboBox = wx.lib.maskededit.MaskedComboBox
|
wxIpAddrCtrl = wx.lib.masked.IpAddrCtrl
|
||||||
wxMaskedComboBoxSelectEvent = wx.lib.maskededit.MaskedComboBoxSelectEvent
|
|
||||||
wxMaskedEditMixin = wx.lib.maskededit.MaskedEditMixin
|
|
||||||
wxMaskedTextCtrl = wx.lib.maskededit.MaskedTextCtrl
|
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
## 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.
|
## 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
|
EVT_MASKEDNUM = wx.lib.masked.numctrl.EVT_NUM
|
||||||
wxMaskedNumCtrl = wx.lib.maskednumctrl.MaskedNumCtrl
|
wxMaskedNumCtrl = wx.lib.masked.numctrl.NumCtrl
|
||||||
wxMaskedNumNumberUpdatedEvent = wx.lib.maskednumctrl.MaskedNumNumberUpdatedEvent
|
wxMaskedNumNumberUpdatedEvent = wx.lib.masked.numctrl.NumberUpdatedEvent
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
## backwards compatibility. Some names will also have a 'wx' added on if
|
## 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.
|
## 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
|
EVT_TIMEUPDATE = wx.lib.masked.timectrl.EVT_TIMEUPDATE
|
||||||
TimeUpdatedEvent = wx.lib.timectrl.TimeUpdatedEvent
|
TimeUpdatedEvent = wx.lib.masked.timectrl.TimeUpdatedEvent
|
||||||
wxTimeCtrl = wx.lib.timectrl.TimeCtrl
|
wxTimeCtrl = wx.lib.masked.timectrl.TimeCtrl
|
||||||
|
Loading…
Reference in New Issue
Block a user