Patches from Will Sadkin:
MaskedEditMixin: - fixed size calculations on changing fonts - fixed tabbing logic now that tab events are entered into the control by default (ie event.Skip()) if wx.TE_PROCESS_TAB is set - fixed code attempting to fix the selection after focus events generated on control destruction, to prevent tracebacks TextCtrl, ComboBox - Added support for XRC - Fixed sizing calculation code - Added SetFont() override method that will recalculate the size if this is called. - Added AppendItems() for ComboBox NumCtrl: - prevented ctrl from accepting same grouping and decimal character, - fixed issue preventing paste from working if decimal char was different than '.' TimeCtrl: - Fixed default value to use 24hour time (which will be converted appropriately if format supports it, and added code to check if local timezone uses "AM/PM" for this concept; if not, control now defaults to 24hour format, and disallows the am/pm form. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@28400 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
parent
b591b7cf2b
commit
339983ff62
@ -12,8 +12,8 @@
|
||||
|
||||
# import relevant external symbols into package namespace:
|
||||
from maskededit import *
|
||||
from textctrl import BaseMaskedTextCtrl, TextCtrl
|
||||
from combobox import BaseMaskedComboBox, ComboBox, MaskedComboBoxSelectEvent
|
||||
from textctrl import BaseMaskedTextCtrl, PreMaskedTextCtrl, TextCtrl
|
||||
from combobox import BaseMaskedComboBox, PreMaskedComboBox, 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
|
||||
|
@ -54,11 +54,6 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
|
||||
**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
|
||||
@ -80,15 +75,56 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
|
||||
choices=choices, style=style|wx.WANTS_CHARS,
|
||||
validator=validator,
|
||||
name=name)
|
||||
|
||||
self.controlInitialized = True
|
||||
|
||||
self._PostInit(style=style, setupEventHandling=setupEventHandling,
|
||||
name=name, value=value, **kwargs)
|
||||
|
||||
|
||||
def _PostInit(self, style=wx.CB_DROPDOWN,
|
||||
setupEventHandling = True, ## setup event handling by default):
|
||||
name = "maskedComboBox", value='', **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
|
||||
|
||||
if not hasattr(self, 'controlInitialized'):
|
||||
|
||||
self.controlInitialized = True ## must have been called via XRC, therefore base class is constructed
|
||||
if not kwargs.has_key('choices'):
|
||||
choices=[]
|
||||
kwargs['choices'] = choices ## set up maskededit to work with choice list too
|
||||
self._choices = []
|
||||
|
||||
## 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.Clear(self)
|
||||
wx.ComboBox.AppendItems(self, choices)
|
||||
|
||||
|
||||
# Set control font - fixed width by default
|
||||
self._setFont()
|
||||
|
||||
if self._autofit:
|
||||
self.SetClientSize(self._CalcSize())
|
||||
self.SetSizeHints(self.GetSize())
|
||||
width = self.GetSize().width
|
||||
height = self.GetBestSize().height
|
||||
self.SetSize((width, height))
|
||||
self.SetSizeHints((width, height))
|
||||
|
||||
|
||||
if value:
|
||||
# ensure value is width of the mask of the control:
|
||||
@ -134,6 +170,19 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
|
||||
return (size[0]+20, size[1])
|
||||
|
||||
|
||||
def SetFont(self, *args, **kwargs):
|
||||
""" Set the font, then recalculate control size, if appropriate. """
|
||||
wx.ComboBox.SetFont(self, *args, **kwargs)
|
||||
if self._autofit:
|
||||
dbg('calculated size:', self._CalcSize())
|
||||
self.SetClientSize(self._CalcSize())
|
||||
width = self.GetSize().width
|
||||
height = self.GetBestSize().height
|
||||
dbg('setting client size to:', (width, height))
|
||||
self.SetSize((width, height))
|
||||
self.SetSizeHints((width, height))
|
||||
|
||||
|
||||
def _GetSelection(self):
|
||||
"""
|
||||
Allow mixin to get the text selection of this control.
|
||||
@ -306,7 +355,6 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
|
||||
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
|
||||
@ -352,6 +400,13 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
|
||||
wx.ComboBox.Append(self, choice, clientData)
|
||||
|
||||
|
||||
def AppendItems( self, choices ):
|
||||
"""
|
||||
AppendItems() is handled in terms of Append, to avoid code replication.
|
||||
"""
|
||||
for choice in choices:
|
||||
self.Append(choice)
|
||||
|
||||
|
||||
def Clear( self ):
|
||||
"""
|
||||
@ -544,3 +599,30 @@ class ComboBox( BaseMaskedComboBox, MaskedEditAccessorsMixin ):
|
||||
pass
|
||||
|
||||
|
||||
class PreMaskedComboBox( BaseMaskedComboBox, MaskedEditAccessorsMixin ):
|
||||
"""
|
||||
This allows us to use XRC subclassing.
|
||||
"""
|
||||
# This should really be wx.EVT_WINDOW_CREATE but it is not
|
||||
# currently delivered for native controls on all platforms, so
|
||||
# we'll use EVT_SIZE instead. It should happen shortly after the
|
||||
# control is created as the control is set to its "best" size.
|
||||
_firstEventType = wx.EVT_SIZE
|
||||
|
||||
def __init__(self):
|
||||
pre = wx.PreComboBox()
|
||||
self.PostCreate(pre)
|
||||
self.Bind(self._firstEventType, self.OnCreate)
|
||||
|
||||
|
||||
def OnCreate(self, evt):
|
||||
self.Unbind(self._firstEventType)
|
||||
self._PostInit()
|
||||
|
||||
i=0
|
||||
## CHANGELOG:
|
||||
## ====================
|
||||
## Version 1.1
|
||||
## 1. Added .SetFont() method that properly resizes control
|
||||
## 2. Modified control to support construction via XRC mechanism.
|
||||
## 3. Added AppendItems() to conform with latest combobox.
|
||||
|
@ -757,7 +757,7 @@ import wx
|
||||
from wx.tools.dbg import Logger
|
||||
|
||||
dbg = Logger()
|
||||
##dbg(enable=0)
|
||||
##dbg(enable=1)
|
||||
|
||||
## ---------- ---------- ---------- ---------- ---------- ---------- ----------
|
||||
|
||||
@ -1914,9 +1914,13 @@ class MaskedEditMixin:
|
||||
self._prevValue = newvalue # disallow undo of sign type
|
||||
|
||||
if self._autofit:
|
||||
## dbg('setting client size to:', self._CalcSize())
|
||||
## dbg('calculated size:', self._CalcSize())
|
||||
self.SetClientSize(self._CalcSize())
|
||||
self.SetSizeHints(self.GetSize())
|
||||
width = self.GetSize().width
|
||||
height = self.GetBestSize().height
|
||||
## dbg('setting client size to:', (width, height))
|
||||
self.SetSize((width, height))
|
||||
self.SetSizeHints((width, height))
|
||||
|
||||
# Set value/type-specific formatting
|
||||
self._applyFormatting()
|
||||
@ -1991,8 +1995,24 @@ class MaskedEditMixin:
|
||||
self._SetInitialValue()
|
||||
|
||||
if self._autofit:
|
||||
# this is tricky, because, as Robin explains:
|
||||
# "Basically there are two sizes to deal with, that are potentially
|
||||
# different. The client size is the inside size and may, depending
|
||||
# on platform, exclude the borders and such. The normal size is
|
||||
# the outside size that does include the borders. What you are
|
||||
# calculating (in _CalcSize) is the client size, but the sizers
|
||||
# deal with the full size and so that is the minimum size that
|
||||
# we need to set with SetSizeHints. The root of the problem is
|
||||
# that in _calcSize the current client size height is returned,
|
||||
# instead of a height based on the current font. So I suggest using
|
||||
# _calcSize to just get the width, and then use GetBestSize to
|
||||
# get the height."
|
||||
self.SetClientSize(self._CalcSize())
|
||||
self.SetSizeHints(self.GetSize())
|
||||
width = self.GetSize().width
|
||||
height = self.GetBestSize().height
|
||||
self.SetSize((width, height))
|
||||
self.SetSizeHints((width, height))
|
||||
|
||||
|
||||
# Set value/type-specific formatting
|
||||
self._applyFormatting()
|
||||
@ -2642,7 +2662,7 @@ class MaskedEditMixin:
|
||||
sizing_text += 'M'
|
||||
#### dbg('len(sizing_text):', len(sizing_text), 'sizing_text: "%s"' % sizing_text)
|
||||
w, h = self.GetTextExtent(sizing_text)
|
||||
size = (w+4, self.GetClientSize().height)
|
||||
size = (w+4, self.GetSize().height)
|
||||
#### dbg('size:', size, indent=0)
|
||||
return size
|
||||
|
||||
@ -2690,7 +2710,7 @@ class MaskedEditMixin:
|
||||
## dbg('ignoring bogus text change event', indent=0)
|
||||
pass
|
||||
else:
|
||||
## dbg('curvalue: "%s", newvalue: "%s"' % (self._curValue, newvalue))
|
||||
## dbg('curvalue: "%s", newvalue: "%s", len(newvalue): %d' % (self._curValue, newvalue, len(newvalue)))
|
||||
if self._Change():
|
||||
if self._signOk and self._isNeg and newvalue.find('-') == -1 and newvalue.find('(') == -1:
|
||||
## dbg('clearing self._isNeg')
|
||||
@ -2864,6 +2884,8 @@ class MaskedEditMixin:
|
||||
if newfield != field and newfield._selectOnFieldEntry:
|
||||
## dbg('queuing selection: (%d, %d)' % (newfield._extent[0], newfield._extent[1]))
|
||||
wx.CallAfter(self._SetSelection, newfield._extent[0], newfield._extent[1])
|
||||
else:
|
||||
wx.CallAfter(self._SetSelection, newpos, new_select_to)
|
||||
keep_processing = False
|
||||
|
||||
elif keep_processing:
|
||||
@ -3400,12 +3422,12 @@ class MaskedEditMixin:
|
||||
|
||||
def _OnReturn(self, event):
|
||||
"""
|
||||
Changes the event to look like a tab event, so we can then call
|
||||
event.Skip() on it, and have the parent form "do the right thing."
|
||||
Swallows the return, issues a Navigate event instead, since
|
||||
masked controls are "single line" by defn.
|
||||
"""
|
||||
## dbg('MaskedEditMixin::OnReturn')
|
||||
event.m_keyCode = wx.WXK_TAB
|
||||
event.Skip()
|
||||
self.Navigate(True)
|
||||
return False
|
||||
|
||||
|
||||
def _OnHome(self,event):
|
||||
@ -3486,9 +3508,6 @@ class MaskedEditMixin:
|
||||
"""
|
||||
Primarily handles TAB events, but can be used for any key that
|
||||
designer wants to change fields within a masked edit control.
|
||||
NOTE: at the moment, although coded to handle shift-TAB and
|
||||
control-shift-TAB, these events are not sent to the controls
|
||||
by the framework.
|
||||
"""
|
||||
## dbg('MaskedEditMixin::_OnChangeField', indent = 1)
|
||||
# determine end of current field:
|
||||
@ -3500,7 +3519,10 @@ class MaskedEditMixin:
|
||||
self._AdjustField(pos)
|
||||
if event.GetKeyCode() == wx.WXK_TAB:
|
||||
## dbg('tab to next ctrl')
|
||||
event.Skip()
|
||||
# As of 2.5.2, you don't call event.Skip() to do
|
||||
# this, but instead force explicit navigation, if
|
||||
# wx.TE_PROCESS_TAB is used (like in the masked edits)
|
||||
self.Navigate(True)
|
||||
#else: do nothing
|
||||
## dbg(indent=0)
|
||||
return False
|
||||
@ -3534,7 +3556,10 @@ class MaskedEditMixin:
|
||||
self._AdjustField(pos)
|
||||
if event.GetKeyCode() == wx.WXK_TAB:
|
||||
## dbg('tab to previous ctrl')
|
||||
event.Skip()
|
||||
# As of 2.5.2, you don't call event.Skip() to do
|
||||
# this, but instead force explicit navigation, if
|
||||
# wx.TE_PROCESS_TAB is used (like in the masked edits)
|
||||
self.Navigate(False)
|
||||
else:
|
||||
## dbg('position at beginning')
|
||||
wx.CallAfter(self._SetInsertionPoint, field_start)
|
||||
@ -3580,7 +3605,10 @@ class MaskedEditMixin:
|
||||
self._AdjustField(pos)
|
||||
if event.GetKeyCode() == wx.WXK_TAB:
|
||||
## dbg('tab to next ctrl')
|
||||
event.Skip()
|
||||
# As of 2.5.2, you don't call event.Skip() to do
|
||||
# this, but instead force explicit navigation, if
|
||||
# wx.TE_PROCESS_TAB is used (like in the masked edits)
|
||||
self.Navigate(True)
|
||||
else:
|
||||
## dbg('position at end')
|
||||
wx.CallAfter(self._SetInsertionPoint, field_end)
|
||||
@ -3594,7 +3622,10 @@ class MaskedEditMixin:
|
||||
self._AdjustField(pos)
|
||||
if event.GetKeyCode() == wx.WXK_TAB:
|
||||
## dbg('tab to next ctrl')
|
||||
event.Skip()
|
||||
# As of 2.5.2, you don't call event.Skip() to do
|
||||
# this, but instead force explicit navigation, if
|
||||
# wx.TE_PROCESS_TAB is used (like in the masked edits)
|
||||
self.Navigate(True)
|
||||
#else: do nothing
|
||||
## dbg(indent=0)
|
||||
return False
|
||||
@ -3640,6 +3671,8 @@ class MaskedEditMixin:
|
||||
if fraction._selectOnFieldEntry:
|
||||
## dbg('queuing selection after decimal point to:', (start, end))
|
||||
wx.CallAfter(self._SetSelection, start, end)
|
||||
else:
|
||||
wx.CallAfter(self._SetSelection, start, start)
|
||||
keep_processing = False
|
||||
|
||||
if self._isInt: ## handle integer value, truncate from current position
|
||||
@ -3654,6 +3687,7 @@ class MaskedEditMixin:
|
||||
if newstr.find(')') != -1:
|
||||
newpos -= 1 # (don't move past right paren)
|
||||
wx.CallAfter(self._SetInsertionPoint, newpos)
|
||||
wx.CallAfter(self._SetSelection, newpos, newpos)
|
||||
keep_processing = False
|
||||
## dbg(indent=0)
|
||||
|
||||
@ -3949,6 +3983,7 @@ class MaskedEditMixin:
|
||||
pos = pos+2
|
||||
|
||||
if newvalue != value:
|
||||
## dbg('old value: "%s"\nnew value: "%s"' % (value, newvalue))
|
||||
self._SetValue(newvalue)
|
||||
self._SetInsertionPoint(pos)
|
||||
|
||||
@ -4042,6 +4077,8 @@ class MaskedEditMixin:
|
||||
self._SetInsertionPoint(pos)
|
||||
if pos < sel_to: # restore selection
|
||||
self._SetSelection(pos, sel_to)
|
||||
else:
|
||||
self._SetSelection(pos, pos)
|
||||
## dbg('adjusted pos:', pos, indent=0)
|
||||
return pos
|
||||
|
||||
@ -5182,7 +5219,10 @@ class MaskedEditMixin:
|
||||
the control, and deselect.
|
||||
"""
|
||||
## dbg('MaskedEditMixin::_fixSelection', indent=1)
|
||||
if not self._mask or not self._IsEditable():
|
||||
# can get here if called with wx.CallAfter after underlying
|
||||
# control has been destroyed on close, but after focus
|
||||
# events
|
||||
if not self or not self._mask or not self._IsEditable():
|
||||
## dbg(indent=0)
|
||||
return
|
||||
|
||||
@ -6396,6 +6436,8 @@ i=1
|
||||
## chars properly.)
|
||||
## 4. Fixed autoselect behavior to work similarly to (2) above, so that combobox
|
||||
## selection will only select the non-empty text, as per request.
|
||||
## 5. Fixed tabbing to work with 2.5.2 semantics.
|
||||
## 6. Fixed size calculation to handle changing fonts
|
||||
##
|
||||
## Version 1.6
|
||||
## 1. Reorganized masked controls into separate package, renamed things accordingly
|
||||
|
@ -386,7 +386,7 @@ MININT = -maxint-1
|
||||
from wx.tools.dbg import Logger
|
||||
from wx.lib.masked import MaskedEditMixin, Field, BaseMaskedTextCtrl
|
||||
dbg = Logger()
|
||||
##dbg(enable=0)
|
||||
##dbg(enable=1)
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
@ -654,14 +654,26 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
|
||||
|
||||
maskededit_kwargs['mask'] = intmask+fracmask
|
||||
|
||||
if kwargs.has_key('groupChar'):
|
||||
if kwargs.has_key('groupChar') or kwargs.has_key('decimalChar'):
|
||||
old_groupchar = self._groupChar # save so we can reformat properly
|
||||
## dbg("old_groupchar: '%s'" % old_groupchar)
|
||||
maskededit_kwargs['groupChar'] = kwargs['groupChar']
|
||||
if kwargs.has_key('decimalChar'):
|
||||
old_decimalchar = self._decimalChar
|
||||
## dbg("old_groupchar: '%s'" % old_groupchar)
|
||||
## dbg("old_decimalchar: '%s'" % old_decimalchar)
|
||||
maskededit_kwargs['decimalChar'] = kwargs['decimalChar']
|
||||
groupchar = old_groupchar
|
||||
decimalchar = old_decimalchar
|
||||
|
||||
if kwargs.has_key('groupChar'):
|
||||
maskededit_kwargs['groupChar'] = kwargs['groupChar']
|
||||
groupchar = kwargs['groupChar']
|
||||
if kwargs.has_key('decimalChar'):
|
||||
maskededit_kwargs['decimalChar'] = kwargs['decimalChar']
|
||||
decimalchar = kwargs['decimalChar']
|
||||
|
||||
# Add sanity check to make sure these are distinct, and if not,
|
||||
# raise attribute error
|
||||
if groupchar == decimalchar:
|
||||
raise AttributeError('groupChar and decimalChar must be distinct')
|
||||
|
||||
|
||||
# for all other parameters, assign keyword args as appropriate:
|
||||
for key, param_value in kwargs.items():
|
||||
@ -1089,9 +1101,8 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
|
||||
and value[sel_start:sel_to] == self._groupChar ):
|
||||
self.SetInsertionPoint(sel_start)
|
||||
self.SetSelection(sel_start, sel_to+1)
|
||||
|
||||
return BaseMaskedTextCtrl._OnErase(self, event, just_return_value)
|
||||
## dbg(indent=0)
|
||||
return BaseMaskedTextCtrl._OnErase(self, event, just_return_value)
|
||||
|
||||
|
||||
def OnTextChange( self, event ):
|
||||
@ -1146,7 +1157,9 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
|
||||
A ValueError exception will be raised if an invalid value
|
||||
is specified.
|
||||
"""
|
||||
## dbg('NumCtrl::SetValue(%s)' % value, indent=1)
|
||||
BaseMaskedTextCtrl.SetValue( self, self._toGUI(value) )
|
||||
## dbg(indent=0)
|
||||
|
||||
|
||||
def SetIntegerWidth(self, value):
|
||||
@ -1519,7 +1532,7 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
|
||||
Preprocessor for base control paste; if value needs to be right-justified
|
||||
to fit in control, do so prior to paste:
|
||||
"""
|
||||
## dbg('NumCtrl::_Paste (value = "%s")' % value)
|
||||
## dbg('NumCtrl::_Paste (value = "%s")' % value, indent=1)
|
||||
if value is None:
|
||||
paste_text = self._getClipboardContents()
|
||||
else:
|
||||
@ -1533,7 +1546,7 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
|
||||
#
|
||||
field = self._FindField(sel_start)
|
||||
edit_start, edit_end = field._extent
|
||||
paste_text = paste_text.replace(self._groupChar, '').replace(self._decimalChar, '.').replace('(', '-').replace(')','')
|
||||
paste_text = paste_text.replace(self._groupChar, '').replace('(', '-').replace(')','')
|
||||
if field._insertRight and self._groupDigits:
|
||||
# want to paste to the left; see if it will fit:
|
||||
left_text = old_value[edit_start:sel_start].lstrip()
|
||||
@ -1547,8 +1560,8 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
|
||||
sel_start += sel_to - orig_sel_start # decrease by amount selected
|
||||
else:
|
||||
## dbg("won't fit left;", 'paste text remains: "%s"' % paste_text)
|
||||
## dbg('adjusted start before accounting for grouping:', sel_start)
|
||||
## dbg('adjusted paste_text before accounting for grouping: "%s"' % paste_text)
|
||||
## dbg('adjusted start before accounting for grouping:', sel_start)
|
||||
## dbg('adjusted paste_text before accounting for grouping: "%s"' % paste_text)
|
||||
pass
|
||||
if self._groupDigits and sel_start != orig_sel_start:
|
||||
left_len = len(old_value[:sel_to].lstrip())
|
||||
@ -1564,12 +1577,6 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
|
||||
self.SetInsertionPoint(sel_to)
|
||||
self.SetSelection(sel_start, sel_to)
|
||||
|
||||
## # treat paste as "replace number", if appropriate:
|
||||
## sel_start, sel_to = self._GetSelection()
|
||||
## if sel_start == sel_to or self._selectOnEntry and (sel_start, sel_to) == self._fields[0]._extent:
|
||||
## paste_text = self._toGUI(paste_text)
|
||||
## self._SetSelection(0, len(self._mask))
|
||||
|
||||
new_text, replace_to = MaskedEditMixin._Paste(self,
|
||||
paste_text,
|
||||
raise_on_invalid=raise_on_invalid,
|
||||
|
@ -22,7 +22,7 @@ from wx.lib.masked import *
|
||||
# be a good place to implement the 2.3 logger class
|
||||
from wx.tools.dbg import Logger
|
||||
dbg = Logger()
|
||||
##dbg(enable=0)
|
||||
##dbg(enable=1)
|
||||
|
||||
# ## TRICKY BIT: to avoid a ton of boiler-plate, and to
|
||||
# ## automate the getter/setter generation for each valid
|
||||
@ -76,6 +76,13 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
|
||||
style=style, validator=validator,
|
||||
name=name)
|
||||
|
||||
self._PostInit(setupEventHandling = setupEventHandling,
|
||||
name=name, value=value,**kwargs )
|
||||
|
||||
|
||||
def _PostInit(self,setupEventHandling=True,
|
||||
name='maskedTextCtrl' , value='', **kwargs):
|
||||
|
||||
self.controlInitialized = True
|
||||
MaskedEditMixin.__init__( self, name, **kwargs )
|
||||
|
||||
@ -219,6 +226,18 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
|
||||
wx.CallAfter(self._SetSelection, replace_to, replace_to)
|
||||
## dbg(indent=0)
|
||||
|
||||
def SetFont(self, *args, **kwargs):
|
||||
""" Set the font, then recalculate control size, if appropriate. """
|
||||
wx.TextCtrl.SetFont(self, *args, **kwargs)
|
||||
if self._autofit:
|
||||
## dbg('calculated size:', self._CalcSize())
|
||||
self.SetClientSize(self._CalcSize())
|
||||
width = self.GetSize().width
|
||||
height = self.GetBestSize().height
|
||||
## dbg('setting client size to:', (width, height))
|
||||
self.SetSize((width, height))
|
||||
self.SetSizeHints((width, height))
|
||||
|
||||
|
||||
def Clear(self):
|
||||
""" Blanks the current control value by replacing it with the default value."""
|
||||
@ -324,3 +343,29 @@ class TextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ):
|
||||
pass
|
||||
|
||||
|
||||
class PreMaskedTextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ):
|
||||
"""
|
||||
This allows us to use XRC subclassing.
|
||||
"""
|
||||
# This should really be wx.EVT_WINDOW_CREATE but it is not
|
||||
# currently delivered for native controls on all platforms, so
|
||||
# we'll use EVT_SIZE instead. It should happen shortly after the
|
||||
# control is created as the control is set to its "best" size.
|
||||
_firstEventType = wx.EVT_SIZE
|
||||
|
||||
def __init__(self):
|
||||
pre = wx.PreTextCtrl()
|
||||
self.PostCreate(pre)
|
||||
self.Bind(self._firstEventType, self.OnCreate)
|
||||
|
||||
|
||||
def OnCreate(self, evt):
|
||||
self.Unbind(self._firstEventType)
|
||||
self._PostInit()
|
||||
|
||||
i=0
|
||||
## CHANGELOG:
|
||||
## ====================
|
||||
## Version 1.1
|
||||
## 1. Added .SetFont() method that properly resizes control
|
||||
## 2. Modified control to support construction via XRC mechanism.
|
||||
|
@ -59,7 +59,7 @@ Here's the API for TimeCtrl:
|
||||
<DL><PRE>
|
||||
<B>TimeCtrl</B>(
|
||||
parent, id = -1,
|
||||
<B>value</B> = '12:00:00 AM',
|
||||
<B>value</B> = '00:00:00',
|
||||
pos = wx.DefaultPosition,
|
||||
size = wx.DefaultSize,
|
||||
<B>style</B> = wxTE_PROCESS_TAB,
|
||||
@ -82,7 +82,10 @@ Here's the API for TimeCtrl:
|
||||
with SetValue() after instantiation of the control.)
|
||||
<DL><B>size</B>
|
||||
<DD>The size of the control will be automatically adjusted for 12/24 hour format
|
||||
if wx.DefaultSize is specified.
|
||||
if wx.DefaultSize is specified. NOTE: due to a problem with wx.DateTime, if the
|
||||
locale does not use 'AM/PM' for its values, the default format will automatically
|
||||
change to 24 hour format, and an AttributeError will be thrown if a non-24 format
|
||||
is specified.
|
||||
<DT><B>style</B>
|
||||
<DD>By default, TimeCtrl will process TAB events, by allowing tab to the
|
||||
different cells within the control.
|
||||
@ -95,7 +98,7 @@ Here's the API for TimeCtrl:
|
||||
<DD>This parameter can be used instead of the fmt24hr and displaySeconds
|
||||
parameters, respectively; it provides a shorthand way to specify the time
|
||||
format you want. Accepted values are 'HHMMSS', 'HHMM', '24HHMMSS', and
|
||||
'24HHMM'. If the format is specified, the other two arguments will be ignored.
|
||||
'24HHMM'. If the format is specified, the other two arguments will be ignored.
|
||||
<BR>
|
||||
<DT><B>fmt24hr</B>
|
||||
<DD>If True, control will display time in 24 hour time format; if False, it will
|
||||
@ -337,7 +340,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
||||
}
|
||||
|
||||
def __init__ (
|
||||
self, parent, id=-1, value = '12:00:00 AM',
|
||||
self, parent, id=-1, value = '00:00:00',
|
||||
pos = wx.DefaultPosition, size = wx.DefaultSize,
|
||||
fmt24hr=False,
|
||||
spinButton = None,
|
||||
@ -348,6 +351,15 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
||||
|
||||
# set defaults for control:
|
||||
## dbg('setting defaults:')
|
||||
|
||||
self.__fmt24hr = False
|
||||
wxdt = wx.DateTimeFromDMY(1, 0, 1970)
|
||||
if wxdt.Format('%p') != 'AM':
|
||||
TimeCtrl.valid_ctrl_params['format'] = '24HHMMSS'
|
||||
self.__fmt24hr = True
|
||||
fmt24hr = True # force/change default positional argument
|
||||
# (will countermand explicit set to False too.)
|
||||
|
||||
for key, param_value in TimeCtrl.valid_ctrl_params.items():
|
||||
# This is done this way to make setattr behave consistently with
|
||||
# "private attribute" name mangling
|
||||
@ -367,7 +379,6 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
||||
kwargs['displaySeconds'] = True
|
||||
|
||||
# (handle positional arg (from original release) differently from rest of kwargs:)
|
||||
self.__fmt24hr = False
|
||||
if not kwargs.has_key('format'):
|
||||
if fmt24hr:
|
||||
if kwargs.has_key('displaySeconds') and kwargs['displaySeconds']:
|
||||
@ -449,7 +460,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
||||
self.SetLimited(limited)
|
||||
self.SetValue(value)
|
||||
except:
|
||||
self.SetValue('12:00:00 AM')
|
||||
self.SetValue('00:00:00')
|
||||
|
||||
if spinButton:
|
||||
self.BindSpinButton(spinButton) # bind spin button up/down events to this control
|
||||
@ -472,6 +483,12 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
||||
raise AttributeError('invalid keyword argument "%s"' % key)
|
||||
|
||||
if key == 'format':
|
||||
wxdt = wx.DateTimeFromDMY(1, 0, 1970)
|
||||
if wxdt.Format('%p') != 'AM':
|
||||
require24hr = True
|
||||
else:
|
||||
require24hr = False
|
||||
|
||||
# handle both local or generic 'maskededit' autoformat codes:
|
||||
if param_value == 'HHMMSS' or param_value == 'TIMEHHMMSS':
|
||||
self.__displaySeconds = True
|
||||
@ -487,6 +504,10 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
||||
self.__fmt24hr = True
|
||||
else:
|
||||
raise AttributeError('"%s" is not a valid format' % param_value)
|
||||
|
||||
if require24hr and not self.__fmt24hr:
|
||||
raise AttributeError('"%s" is an unsupported time format for the current locale' % param_value)
|
||||
|
||||
reset_format = True
|
||||
|
||||
elif key in ("displaySeconds", "display_seconds") and not kwargs.has_key('format'):
|
||||
@ -552,7 +573,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
||||
self.SetLimited(limited)
|
||||
self.SetValue(value)
|
||||
except:
|
||||
self.SetValue('12:00:00 AM')
|
||||
self.SetValue('00:00:00')
|
||||
## dbg(indent=0)
|
||||
return {} # no arguments to return
|
||||
else:
|
||||
@ -663,8 +684,13 @@ class TimeCtrl(BaseMaskedTextCtrl):
|
||||
## dbg('checkTime == len(value)?', valid)
|
||||
|
||||
if not valid:
|
||||
# deal with bug/deficiency in wx.DateTime:
|
||||
if wxdt.Format('%p') not in ('AM', 'PM') and checkTime in (5,8):
|
||||
# couldn't parse the AM/PM field
|
||||
raise ValueError('cannot convert string "%s" to valid time for the current locale; please use 24hr time instead' % value)
|
||||
else:
|
||||
## 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:
|
||||
if isinstance(value, wx.DateTime):
|
||||
|
Loading…
Reference in New Issue
Block a user