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:
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
|
||||
|
||||
|
109
wxPython/wx/lib/masked/ctrl.py
Normal file
109
wxPython/wx/lib/masked/ctrl.py
Normal file
@@ -0,0 +1,109 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: wxPython.lib.masked.ctrl.py
|
||||
# Author: Will Sadkin
|
||||
# Created: 09/24/2003
|
||||
# Copyright: (c) 2003 by Will Sadkin
|
||||
# RCS-ID: $Id$
|
||||
# License: wxWindows license
|
||||
#----------------------------------------------------------------------------
|
||||
# 12/09/2003 - Jeff Grimmett (grimmtooth@softhome.net)
|
||||
#
|
||||
# o Updated for wx namespace (minor)
|
||||
#
|
||||
# 12/20/2003 - Jeff Grimmett (grimmtooth@softhome.net)
|
||||
#
|
||||
# o Removed wx prefix
|
||||
#
|
||||
|
||||
"""<html><body>
|
||||
<P>
|
||||
<B>masked.Ctrl</B> is actually a factory function for several types of
|
||||
masked edit controls:
|
||||
<P>
|
||||
<UL>
|
||||
<LI><b>masked.TextCtrl</b> - standard masked edit text box</LI>
|
||||
<LI><b>masked.ComboBox</b> - adds combobox capabilities</LI>
|
||||
<LI><b>masked.IpAddrCtrl</b> - adds logical input semantics for IP address entry</LI>
|
||||
<LI><b>masked.TimeCtrl</b> - special subclass handling lots of time formats as values</LI>
|
||||
<LI><b>masked.NumCtrl</b> - special subclass handling numeric values</LI>
|
||||
</UL>
|
||||
<P>
|
||||
<B>masked.Ctrl</B> works by looking for a special <b><i>controlType</i></b>
|
||||
parameter in the variable arguments of the control, to determine
|
||||
what kind of instance to return.
|
||||
controlType can be one of:
|
||||
<PRE><FONT SIZE=-1>
|
||||
controlTypes.TEXT
|
||||
controlTypes.COMBO
|
||||
controlTypes.IPADDR
|
||||
controlTypes.TIME
|
||||
controlTypes.NUMBER
|
||||
</FONT></PRE>
|
||||
These constants are also available individually, ie, you can
|
||||
use either of the following:
|
||||
<PRE><FONT SIZE=-1>
|
||||
from wxPython.wx.lib.masked import Ctrl, COMBO, TEXT, NUMBER, TIME
|
||||
from wxPython.wx.lib.masked import Ctrl, controlTypes
|
||||
</FONT></PRE>
|
||||
If not specified as a keyword argument, the default controlType is
|
||||
controlTypes.TEXT.
|
||||
<P>
|
||||
Each of the above classes has its own unique arguments, but MaskedCtrl
|
||||
provides a single "unified" interface for masked controls. Masked.TextCtrl,
|
||||
masked.ComboBox and masked.IpAddrCtrl are all documented below; the others have
|
||||
their own demo pages and interface descriptions.
|
||||
</body></html>
|
||||
"""
|
||||
|
||||
from wx.lib.masked import TextCtrl, ComboBox, IpAddrCtrl
|
||||
from wx.lib.masked import NumCtrl
|
||||
from wx.lib.masked import TimeCtrl
|
||||
|
||||
|
||||
# "type" enumeration for class instance factory function
|
||||
TEXT = 0
|
||||
COMBO = 1
|
||||
IPADDR = 2
|
||||
TIME = 3
|
||||
NUMBER = 4
|
||||
|
||||
# for ease of import
|
||||
class controlTypes:
|
||||
TEXT = TEXT
|
||||
COMBO = COMBO
|
||||
IPADDR = IPADDR
|
||||
TIME = TIME
|
||||
NUMBER = NUMBER
|
||||
|
||||
|
||||
def Ctrl( *args, **kwargs):
|
||||
"""
|
||||
Actually a factory function providing a unifying
|
||||
interface for generating masked controls.
|
||||
"""
|
||||
if not kwargs.has_key('controlType'):
|
||||
controlType = TEXT
|
||||
else:
|
||||
controlType = kwargs['controlType']
|
||||
del kwargs['controlType']
|
||||
|
||||
if controlType == TEXT:
|
||||
return TextCtrl(*args, **kwargs)
|
||||
|
||||
elif controlType == COMBO:
|
||||
return ComboBox(*args, **kwargs)
|
||||
|
||||
elif controlType == IPADDR:
|
||||
return IpAddrCtrl(*args, **kwargs)
|
||||
|
||||
elif controlType == TIME:
|
||||
return TimeCtrl(*args, **kwargs)
|
||||
|
||||
elif controlType == NUMBER:
|
||||
return NumCtrl(*args, **kwargs)
|
||||
|
||||
else:
|
||||
raise AttributeError(
|
||||
"invalid controlType specified: %s" % repr(controlType))
|
||||
|
||||
|
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)
|
||||
|
||||
|
6676
wxPython/wx/lib/masked/maskededit.py
Normal file
6676
wxPython/wx/lib/masked/maskededit.py
Normal file
File diff suppressed because it is too large
Load Diff
1616
wxPython/wx/lib/masked/numctrl.py
Normal file
1616
wxPython/wx/lib/masked/numctrl.py
Normal file
File diff suppressed because it is too large
Load Diff
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
|
||||
|
||||
|
1317
wxPython/wx/lib/masked/timectrl.py
Normal file
1317
wxPython/wx/lib/masked/timectrl.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user