diff --git a/wxPython/demo/wxTimeCtrl.py b/wxPython/demo/wxTimeCtrl.py
index b824ff0586..c20117afe3 100644
--- a/wxPython/demo/wxTimeCtrl.py
+++ b/wxPython/demo/wxTimeCtrl.py
@@ -1,78 +1,139 @@
from wxPython.wx import *
from wxPython.lib.timectrl import *
-import string
-
#----------------------------------------------------------------------
-class TestPanel(wxPanel):
- def __init__(self, parent, log):
- wxPanel.__init__(self, parent, -1)
+class TestPanel( wxPanel ):
+ def __init__( self, parent, log ):
+
+ wxPanel.__init__( self, parent, -1 )
self.log = log
- panel = wxPanel(self, -1)
+ panel = wxPanel( self, -1 )
+
grid = wxFlexGridSizer( 0, 2, 20, 0 )
- text1 = wxStaticText( panel, 10, "A 12-hour format wxTimeCtrl:", wxDefaultPosition, wxDefaultSize, 0 )
- grid.AddWindow( text1, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5 )
-
- hsizer1 = wxBoxSizer( wxHORIZONTAL )
- self.time12 = wxTimeCtrl(panel, 20, name="12 hour time")
- hsizer1.AddWindow( self.time12, 0, wxALIGN_CENTRE, 5 )
+ text1 = wxStaticText( panel, 10, "A 12-hour format wxTimeCtrl:")
+ self.time12 = wxTimeCtrl( panel, 20, name="12 hour control" )
spin1 = wxSpinButton( panel, 30, wxDefaultPosition, wxSize(-1,20), 0 )
- self.time12.BindSpinButton(spin1)
- hsizer1.AddWindow( spin1, 0, wxALIGN_CENTRE, 5 )
- grid.AddSizer( hsizer1, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 )
+ self.time12.BindSpinButton( spin1 )
- text2 = wxStaticText( panel, 40, "A 24-hour format wxTimeCtrl:", wxDefaultPosition, wxDefaultSize, 0 )
- grid.AddWindow( text2, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxLEFT|wxTOP|wxBOTTOM, 5 )
+ grid.AddWindow( text1, 0, wxALIGN_RIGHT, 5 )
+ hbox1 = wxBoxSizer( wxHORIZONTAL )
+ hbox1.AddWindow( self.time12, 0, wxALIGN_CENTRE, 5 )
+ hbox1.AddWindow( spin1, 0, wxALIGN_CENTRE, 5 )
+ grid.AddSizer( hbox1, 0, wxLEFT, 5 )
- hsizer2 = wxBoxSizer( wxHORIZONTAL )
- self.time24 = wxTimeCtrl(panel, 50, fmt24hr=true, name="24 hour time")
- hsizer2.AddWindow( self.time24, 0, wxALIGN_CENTRE, 5 )
+
+ text2 = wxStaticText( panel, 40, "A 24-hour format wxTimeCtrl:")
+ self.time24 = wxTimeCtrl( panel, 50, fmt24hr=true, name="24 hour control" )
spin2 = wxSpinButton( panel, 60, wxDefaultPosition, wxSize(-1,20), 0 )
- self.time24.BindSpinButton(spin2)
- hsizer2.AddWindow( spin2, 0, wxALIGN_CENTRE, 5 )
- grid.AddSizer( hsizer2, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 )
+ self.time24.BindSpinButton( spin2 )
- panel.SetAutoLayout(true)
- panel.SetSizer(grid)
- grid.Fit(panel)
- panel.Move((50,50))
+ grid.AddWindow( text2, 0, wxALIGN_RIGHT|wxTOP|wxBOTTOM, 5 )
+ hbox2 = wxBoxSizer( wxHORIZONTAL )
+ hbox2.AddWindow( self.time24, 0, wxALIGN_CENTRE, 5 )
+ hbox2.AddWindow( spin2, 0, wxALIGN_CENTRE, 5 )
+ grid.AddSizer( hbox2, 0, wxLEFT, 5 )
+
+
+ text3 = wxStaticText( panel, 70, "A wxTimeCtrl without a spin button:")
+ self.spinless_ctrl = wxTimeCtrl( panel, 80, name="spinless control" )
+
+ grid.AddWindow( text3, 0, wxALIGN_RIGHT|wxTOP|wxBOTTOM, 5 )
+ grid.AddWindow( self.spinless_ctrl, 0, wxLEFT, 5 )
+
+
+ buttonChange = wxButton( panel, 100, "Change Controls")
+ self.radio12to24 = wxRadioButton( panel, 110, "Copy 12-hour time to 24-hour control", wxDefaultPosition, wxDefaultSize, wxRB_GROUP )
+ self.radio24to12 = wxRadioButton( panel, 120, "Copy 24-hour time to 12-hour control")
+ self.radioWx = wxRadioButton( panel, 130, "Set controls to 'now' using wxDateTime")
+ self.radioMx = wxRadioButton( panel, 140, "Set controls to 'now' using mxDateTime")
+
+ radio_vbox = wxBoxSizer( wxVERTICAL )
+ radio_vbox.AddWindow( self.radio12to24, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 )
+ radio_vbox.AddWindow( self.radio24to12, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 )
+ radio_vbox.AddWindow( self.radioWx, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 )
+ radio_vbox.AddWindow( self.radioMx, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 )
+
+ box_label = wxStaticBox( panel, 90, "Change Controls through API" )
+ buttonbox = wxStaticBoxSizer( box_label, wxHORIZONTAL )
+ buttonbox.AddWindow( buttonChange, 0, wxALIGN_CENTRE|wxALL, 5 )
+ buttonbox.AddSizer( radio_vbox, 0, wxALIGN_CENTRE|wxALL, 5 )
+
+ outer_box = wxBoxSizer( wxVERTICAL )
+ outer_box.AddSizer( grid, 0, wxALIGN_CENTRE|wxBOTTOM, 20 )
+ outer_box.AddSizer( buttonbox, 0, wxALIGN_CENTRE|wxALL, 5 )
+
+
+ # Turn on mxDateTime option only if we can import the module:
+ try:
+ from mx import DateTime
+ except ImportError:
+ self.radioMx.Enable( false )
+
+
+ panel.SetAutoLayout( true )
+ panel.SetSizer( outer_box )
+ outer_box.Fit( panel )
+ panel.Move( (50,50) )
self.panel = panel
- EVT_TIMEUPDATE(self, self.time12.GetId(), self.OnTimeChange)
- EVT_TIMEUPDATE(self, self.time24.GetId(), self.OnTimeChange)
+
+ EVT_TIMEUPDATE( self, self.time12.GetId(), self.OnTimeChange )
+ EVT_TIMEUPDATE( self, self.time24.GetId(), self.OnTimeChange )
+ EVT_TIMEUPDATE( self, self.spinless_ctrl.GetId(), self.OnTimeChange )
+
+ EVT_BUTTON( self, buttonChange.GetId(), self.OnButtonClick )
- def OnTimeChange(self, event):
- timectrl = self.panel.FindWindowById(event.GetId())
- self.log.write('%s = %s\n' % (
- timectrl.GetName(), timectrl.GetValue() ) )
+ def OnTimeChange( self, event ):
+ timectrl = self.panel.FindWindowById( event.GetId() )
+ self.log.write('%s time = %s\n' % ( timectrl.GetName(), timectrl.GetValue() ) )
+ def OnButtonClick( self, event ):
+ if self.radio12to24.GetValue():
+ self.time24.SetValue( self.time12.GetValue() )
+
+ elif self.radio24to12.GetValue():
+ self.time12.SetValue( self.time24.GetValue() )
+
+ elif self.radioWx.GetValue():
+ now = wxDateTime_Now()
+ self.time12.SetWxDateTime( now )
+ self.time24.SetWxDateTime( now )
+ self.spinless_ctrl.SetWxDateTime( now )
+
+ elif self.radioMx.GetValue():
+ from mx import DateTime
+ now = DateTime.now()
+ self.time12.SetMxDateTime( now )
+ self.time24.SetMxDateTime( now )
+ self.spinless_ctrl.SetMxDateTime( now )
#----------------------------------------------------------------------
-def runTest(frame, nb, log):
- win = TestPanel(nb, log)
+def runTest( frame, nb, log ):
+ win = TestPanel( nb, log )
return win
#----------------------------------------------------------------------
-# It's nice to be able to use HTML here, but line breaks in the triple quoted string
-# cause the resulting output to look funny, as they seem to be interpreted by the
-# parser...
-
overview = """
-wxTimeCtrl provides a multi-cell control that allows manipulation of a time value. It supports 12 or 24 hour format, and you can use wxDateTime or mxDateTimet o get/set values from the control.
+wxTimeCtrl provides a multi-cell control that allows manipulation of a time
+value. It supports 12 or 24 hour format, and you can use wxDateTime or mxDateTime
+to get/set values from the control.
-Left/right/tab keys to switch cells within a wxTimeCtrl, and the up/down arrows act like a spin control. wxTimeCtrl also allows for an actual spin button to be attached to the control, so that it acts like the up/down arrow keys.
+Left/right/tab keys to switch cells within a wxTimeCtrl, and the up/down arrows act
+like a spin control. wxTimeCtrl also allows for an actual spin button to be attached
+to the control, so that it acts like the up/down arrow keys.
-Here's the interface for wxTimeCtrl:
-
-
-
-wxTimeCtrl(parent, id = -1,
+The ! key sets the value of the control to now!
+
+Here's the API for wxTimeCtrl:
+
+ wxTimeCtrl(
+ parent, id = -1,
value = '12:00:00 AM',
pos = wxDefaultPosition,
size = wxDefaultSize,
@@ -82,36 +143,71 @@ Here's the interface for wxTimeCtrl:
name = "time")
- - value
- If no initial value is set, the default will be midnight; if an illegal string is specified, a ValueError will result. (You can always later set the initial time with SetValue() after instantiation of the control.)
-
size- The size of the control will be automatically adjusted for 12/24 hour format if wxDefaultSize is specified.
-
- fmt24hr
- If true, control will display time in 24 hour time format; if false, it will use 12 hour AM/PM format. SetValue() will adjust values accordingly for the control, based on the format specified.
-
- spinButton
- If specified, this button's events will be bound to the behavior of the wxTimeCtrl, working like up/down cursor key events. (See BindSpinButton.)
-
- style
- By default, wxTimeCtrl will process TAB events, by allowing tab to the different cells within the control.
+
- value
+
- If no initial value is set, the default will be midnight; if an illegal string
+ is specified, a ValueError will result. (You can always later set the initial time
+ with SetValue() after instantiation of the control.)
+
size
+ - The size of the control will be automatically adjusted for 12/24 hour format
+ if wxDefaultSize is specified.
+
+ - fmt24hr
+
- If true, control will display time in 24 hour time format; if false, it will
+ use 12 hour AM/PM format. SetValue() will adjust values accordingly for the
+ control, based on the format specified.
+
+ - spinButton
+
- If specified, this button's events will be bound to the behavior of the
+ wxTimeCtrl, working like up/down cursor key events. (See BindSpinButton.)
+
+ - style
+
- By default, wxTimeCtrl will process TAB events, by allowing tab to the
+ different cells within the control.
-- SetValue(time_string)
- Sets the value of the control to a particular time, given a valid time string; raises ValueError on invalid value
-
- GetValue()
- Retrieves the string value of the time from the control
- - SetWxDateTime(wxDateTime)
- Uses the time portion of a wxDateTime to construct a value for the control.
-
- GetWxDateTime()
- Retrieves the value of the control, and applies it to the wxDateTimeFromHMS() constructor, and returns the resulting value. (This returns the date portion as "today".)
- - SetMxDateTime(mxDateTime)
- Uses the time portion of an mxDateTime to construct a value for the control. NOTE: This imports mx.DateTime at runtime only if this or the Get function is called.
-
- GetMxDateTime()
- Retrieves the value of the control and applies it to the DateTime.Time() constructor, and returns the resulting value. (mxDateTime is smart enough to know this is just a time value.)
+
- SetValue(time_string)
+
- Sets the value of the control to a particular time, given a valid time string;
+raises ValueError on invalid value
- - BindSpinButton(wxSpinBtton)
- Binds an externally created spin button to the control, so that up/down spin events change the active cell or selection in the control (in addition to the up/down cursor keys.) (This is primarily to allow you to create a "standard" interface to time controls, as seen in Windows.)
+
- GetValue()
+
- Retrieves the string value of the time from the control
- - EVT_TIMEUPDATE(win, id, func)
- func is fired whenever the value of the control changes.
+
- SetWxDateTime(wxDateTime)
+
- Uses the time portion of a wxDateTime to construct a value for the control.
+
+ - GetWxDateTime()
+
- Retrieves the value of the control, and applies it to the wxDateTimeFromHMS()
+constructor, and returns the resulting value. (This returns the date portion as
+"today".)
+
+ - SetMxDateTime(mxDateTime)
+
- Uses the time portion of an mxDateTime to construct a value for the control.
+NOTE: This imports mx.DateTime at runtime only if this or the Get function
+is called.
+
+ - GetMxDateTime()
+
- Retrieves the value of the control and applies it to the DateTime.Time()
+constructor, and returns the resulting value. (mxDateTime is smart enough to
+know this is just a time value.)
+
+ - BindSpinButton(wxSpinBtton)
+
- Binds an externally created spin button to the control, so that up/down spin
+events change the active cell or selection in the control (in addition to the
+up/down cursor keys.) (This is primarily to allow you to create a "standard"
+interface to time controls, as seen in Windows.)
+
+ - EVT_TIMEUPDATE(win, id, func)
+
- func is fired whenever the value of the control changes.
"""
-
if __name__ == '__main__':
import sys,os
import run
run.main(['', os.path.basename(sys.argv[0])])
-
-
diff --git a/wxPython/wxPython/lib/timectrl.py b/wxPython/wxPython/lib/timectrl.py
index 37c15e8c92..d32e17d825 100644
--- a/wxPython/wxPython/lib/timectrl.py
+++ b/wxPython/wxPython/lib/timectrl.py
@@ -3,6 +3,7 @@
# Author: Will Sadkin
# Created: 09/19/2002
# Copyright: (c) 2002 by Will Sadkin, 2002
+# RCS-ID: $Id$
# License: wxWindows license
#----------------------------------------------------------------------------
# NOTE:
@@ -26,22 +27,24 @@ import string
_debug = 0
_indent = 0
-def _dbg(*args, **kwargs):
- global _indent
+if _debug:
+ def _dbg(*args, **kwargs):
+ global _indent
- if _debug:
if len(args):
if _indent: print ' ' * 3 * _indent,
for arg in args: print arg,
print
- # else do nothing
-
- # post process args:
- for kwarg, value in kwargs.items():
- if kwarg == 'indent' and value: _indent = _indent + 1
- elif kwarg == 'indent' and value == 0: _indent = _indent - 1
- if _indent < 0: _indent = 0
+ # else do nothing
+ # post process args:
+ for kwarg, value in kwargs.items():
+ if kwarg == 'indent' and value: _indent = _indent + 1
+ elif kwarg == 'indent' and value == 0: _indent = _indent - 1
+ if _indent < 0: _indent = 0
+else:
+ def _dbg(*args, **kwargs):
+ pass
# This class of event fires whenever the value of the time changes in the control:
@@ -55,10 +58,40 @@ class TimeUpdatedEvent(wxPyCommandEvent):
wxPyCommandEvent.__init__(self, wxEVT_TIMEVAL_UPDATED, id)
self.value = value
def GetValue(self):
- """Retrieve the value of the float control at the time this event was generated"""
+ """Retrieve the value of the time control at the time this event was generated"""
return self.value
+# Set up all the positions of the cells in the wxTimeCtrl (once at module import):
+# Format of control is:
+# hh:mm:ss xM
+# 1
+# positions: 01234567890
+_listCells = ['hour', 'minute', 'second', 'am_pm']
+_listCellRange = [(0,1,2), (3,4,5), (6,7,8), (9,10,11)]
+_listDelimPos = [2,5,8]
+
+# Create dictionary of cell ranges, indexed by name or position in the range:
+_dictCellRange = {}
+for i in range(4):
+ _dictCellRange[_listCells[i]] = _listCellRange[i]
+for cell in _listCells:
+ for i in _dictCellRange[cell]:
+ _dictCellRange[i] = _dictCellRange[cell]
+
+
+# Create lists of starting and ending positions for each range, and a dictionary of starting
+# positions indexed by name
+_listStartCellPos = []
+_listEndCellPos = []
+for tup in _listCellRange:
+ _listStartCellPos.append(tup[0]) # 1st char of cell
+ _listEndCellPos.append(tup[1]) # last char of cell (not including delimiter)
+
+_dictStartCellPos = {}
+for i in range(4):
+ _dictStartCellPos[_listCells[i]] = _listStartCellPos[i]
+
class wxTimeCtrl(wxTextCtrl):
def __init__ (
@@ -72,46 +105,16 @@ class wxTimeCtrl(wxTextCtrl):
pos=pos, size=size, style=style, name=name)
self.__fmt24hr = fmt24hr
+
if size == wxDefaultSize:
# set appropriate default sizes depending on format:
if self.__fmt24hr:
- testText = '00:00:00 '
+ testText = '00:00:00X' # give it a little extra space
else:
- testText = '00:00:00 XXX'
+ testText = '00:00:00 XXX' # give it a little extra space
w, h = self.GetTextExtent(testText)
self.SetClientSize( (w+4, self.GetClientSize().height) )
- # Set up all the positions of the cells in the wxTimeCtrl (once):
- # Format of control is:
- # hh:mm:ss xM
- # 1
- # positions: 01234567890
-
- self.__listCells = ['hour', 'minute', 'second', 'am_pm']
- self.__listCellRange = [(0,1,2), (3,4,5), (6,7,8), (9,10,11)]
- self.__listDelimPos = [2,5,8]
-
- # Create dictionary of cell ranges, indexed by name or position in the range:
- self.__dictCellRange = {}
- for i in range(4):
- self.__dictCellRange[self.__listCells[i]] = self.__listCellRange[i]
-
- for cell in self.__listCells:
- for i in self.__dictCellRange[cell]:
- self.__dictCellRange[i] = self.__dictCellRange[cell]
-
-
- # Create lists of starting and ending positions for each range, and a dictionary of starting
- # positions indexed by name
- self.__listStartCellPos = []
- self.__listEndCellPos = []
- for tup in self.__listCellRange:
- self.__listStartCellPos.append(tup[0]) # 1st char of cell
- self.__listEndCellPos.append(tup[1]) # last char of cell (not including delimiter)
-
- self.__dictStartCellPos = {}
- for i in range(4):
- self.__dictStartCellPos[self.__listCells[i]] = self.__listStartCellPos[i]
if self.__fmt24hr: self.__lastCell = 'second'
else: self.__lastCell = 'am_pm'
@@ -123,27 +126,48 @@ class wxTimeCtrl(wxTextCtrl):
self.SetValue('12:00:00 AM')
# set initial position and selection state
- self.__SetCurrentCell(self.__dictStartCellPos['hour'])
+ self.__SetCurrentCell(_dictStartCellPos['hour'])
self.__bSelection = false
+ self.__posSelectTo = self.__posCurrent
+ # Set up internal event handlers to change the event reaction behavior of
+ # the base wxTextCtrl:
EVT_TEXT(self, self.GetId(), self.__OnTextChange)
EVT_SET_FOCUS(self, self.__OnFocus)
+ EVT_LEFT_UP(self, self.__OnChangePos)
+ EVT_LEFT_DCLICK(self, self.__OnDoubleClick)
EVT_CHAR(self, self.__OnChar)
if spinButton:
self.BindSpinbutton(spinButton) # bind spin button up/down events to this control
+
+ def BindSpinButton(self, sb):
+ """
+ This function binds an externally created spin button to the control, so that
+ up/down events from the button automatically change the control.
+ """
+ _dbg('wxTimeCtrl::BindSpinButton')
+ self.__spinButton = sb
+ if self.__spinButton:
+ # bind event handlers to spin ctrl
+ EVT_SPIN_UP(self.__spinButton, self.__spinButton.GetId(), self.__OnSpinUp)
+ EVT_SPIN_DOWN(self.__spinButton, self.__spinButton.GetId(), self.__OnSpinDown)
+
+
+
def __repr__(self):
return "" % self.GetValue()
+
def SetValue(self, value):
"""
Validating SetValue function for time strings, doing 12/24 format conversion as appropriate.
"""
_dbg('wxTimeCtrl::SetValue', indent=1)
- dict_range = self.__dictCellRange
- dict_start = self.__dictStartCellPos
+ dict_range = _dictCellRange # (for brevity)
+ dict_start = _dictStartCellPos
fmt12len = dict_range['am_pm'][-1]
fmt24len = dict_range['second'][-1]
@@ -169,7 +193,6 @@ class wxTimeCtrl(wxTextCtrl):
if len_ok and hour_ok and min_ok and sec_ok and separators_correct:
_dbg('valid time string')
-
self.__hour = hour
if len(value) == fmt12len: # handle 12 hour format conversion for actual hour:
am = value[dict_start['am_pm']:] == 'AM'
@@ -187,7 +210,6 @@ class wxTimeCtrl(wxTextCtrl):
_dbg('need_to_convert =', need_to_convert)
if need_to_convert: #convert to 12/24 hour format as specified:
- dict_start = self.__dictStartCellPos
if self.__fmt24hr and len(value) == fmt12len:
text = '%.2d:%.2d:%.2d' % (hour, minute, second)
else:
@@ -218,7 +240,7 @@ class wxTimeCtrl(wxTextCtrl):
raise ValueError, 'value is not a valid time string'
_dbg(indent=0)
- def SetFromWxDateTime(self, wxdt):
+ def SetWxDateTime(self, wxdt):
value = '%2d:%.2d:%.2d' % (wxdt.GetHour(), wxdt.GetMinute(), wxdt.GetSecond())
self.SetValue(value)
@@ -236,18 +258,6 @@ class wxTimeCtrl(wxTextCtrl):
t = DateTime.Time(self.__hour, self.__minute, self.__second)
return t
- def BindSpinButton(self, sb):
- """
- This function binds an externally created spin button to the control, so that
- up/down events from the button automatically change the control.
- """
- _dbg('wxTimeCtrl::BindSpinButton')
- self.__spinButton = sb
- if self.__spinButton:
- EVT_SPIN_UP(self.__spinButton, self.__spinButton.GetId(), self.__OnSpinUp) # bind event handler to spin ctrl
- EVT_SPIN_DOWN(self.__spinButton, self.__spinButton.GetId(), self.__OnSpinDown) # bind event handler to spin ctrl
-
-
#-------------------------------------------------------------------------------------------------------------
# these are private functions and overrides:
@@ -256,7 +266,8 @@ class wxTimeCtrl(wxTextCtrl):
Sets state variables that indicate the current cell and position within the control.
"""
self.__posCurrent = pos
- self.__cellStart, self.__cellEnd = self.__dictCellRange[pos][0], self.__dictCellRange[pos][-1]
+ self.__cellStart, self.__cellEnd = _dictCellRange[pos][0], _dictCellRange[pos][-1]
+
def SetInsertionPoint(self, pos):
"""
@@ -266,6 +277,54 @@ class wxTimeCtrl(wxTextCtrl):
wxTextCtrl.SetInsertionPoint(self, pos) # (causes EVT_TEXT event to fire)
+ def SetSelection(self, sel_start, sel_to):
+ _dbg('wxTimeCtrl::SetSelection', sel_start, sel_to)
+ self.__bSelection = sel_start != sel_to
+ self.__posSelectTo = sel_to
+ wxTextCtrl.SetSelection(self, sel_start, sel_to)
+
+
+ def __OnFocus(self,event):
+ """
+ This event handler is currently necessary to work around new default
+ behavior as of wxPython2.3.3;
+ The TAB key auto selects the entire contents of the wxTextCtrl *after*
+ the EVT_SET_FOCUS event occurs; therefore we can't query/adjust the selection
+ *here*, because it hasn't happened yet. So to prevent this behavior, and
+ preserve the correct selection when the focus event is not due to tab,
+ we need to pull the following trick:
+ """
+ _dbg('wxTimeCtrl::OnFocus')
+ wxCallAfter(self.__FixSelection)
+
+
+ def __FixSelection(self):
+ """
+ This gets called after the TAB traversal selection is made, if the
+ focus event was due to this, but before the EVT_LEFT_* events if
+ the focus shift was due to a mouse event.
+
+ The trouble is that, a priori, there's no explicit notification of
+ why the focus event we received. However, the whole reason we need to
+ do this is because the default behavior on TAB traveral in a wxTextCtrl is
+ now to select the entire contents of the window, something we don't want.
+ So we can *now* test the selection range, and if it's "the whole text"
+ we can assume the cause, change the insertion point to the start of
+ the control, and deselect.
+ """
+ _dbg('wxTimeCtrl::FixSelection', indent=1)
+ sel_start, sel_to = self.GetSelection()
+ if sel_start == 0 and sel_to in _dictCellRange[self.__lastCell]:
+ # This isn't normally allowed, and so assume we got here by the new
+ # "tab traversal" behavior, so we need to reset the selection
+ # and insertion point:
+ _dbg('entire text selected; resetting selection to start of control')
+ self.__SetCurrentCell(0)
+ self.SetInsertionPoint(0)
+ self.SetSelection(self.__posCurrent, self.__posCurrent)
+ _dbg(indent=0)
+
+
def __OnTextChange(self, event):
"""
This private event handler is required to retain the current position information of the cursor
@@ -284,17 +343,11 @@ class wxTimeCtrl(wxTextCtrl):
event.Skip()
_dbg(indent=0)
- def __OnFocus(self, event):
- """
- This internal event handler ensures legal setting of input cursor on (re)focus to the control.
- """
- _dbg('wxTimeCtrl::OnFocus; ctrl id=', event.GetId())
- self.__SetCurrentCell(0)
- self.__bSelection = false
- self.__posSelectTo = self.__posCurrent
+ def __OnSpin(self, key):
+ self.IncrementValue(key, self.__posCurrent)
+ self.SetFocus()
self.SetInsertionPoint(self.__posCurrent)
- event.Skip()
-
+ self.SetSelection(self.__posCurrent, self.__posSelectTo)
def __OnSpinUp(self, event):
"""
@@ -302,9 +355,8 @@ class wxTimeCtrl(wxTextCtrl):
causes control to behave as if up arrow was pressed.
"""
_dbg('wxTimeCtrl::OnSpinUp')
- pos = self.GetInsertionPoint()
- self.IncrementValue(WXK_UP, pos)
- self.SetInsertionPoint(pos)
+ self.__OnSpin(WXK_UP)
+
def __OnSpinDown(self, event):
"""
@@ -312,9 +364,42 @@ class wxTimeCtrl(wxTextCtrl):
causes control to behave as if down arrow was pressed.
"""
_dbg('wxTimeCtrl::OnSpinDown')
+ self.__OnSpin(WXK_DOWN)
+
+
+ def __OnChangePos(self, event):
+ """
+ Event handler for left mouse click events; this handler
+ changes limits the selection to the new cell boundaries.
+ """
+ _dbg('wxTimeCtrl::OnChangePos', indent=1)
pos = self.GetInsertionPoint()
- self.IncrementValue(WXK_DOWN, pos)
- self.SetInsertionPoint(pos)
+ self.__SetCurrentCell(pos)
+ sel_start, sel_to = self.GetSelection()
+ selection = sel_start != sel_to
+ if not selection:
+ if pos in _listDelimPos: # disallow position at end of field:
+ self.__posCurrent = pos-1
+ self.__posSelectTo = self.__posCurrent
+ self.__bSelection = false
+ else:
+ # only allow selection to end of current cell:
+ if sel_to < pos: self.__posSelectTo = self.__cellStart
+ elif sel_to > pos: self.__posSelectTo = self.__cellEnd
+
+ _dbg('new pos =', pos, 'select to ', self.__posSelectTo, indent=0)
+ self.SetSelection(self.__posCurrent, self.__posSelectTo)
+ event.Skip()
+
+ def __OnDoubleClick(self, event):
+ """
+ Event handler for left double-click mouse events; this handler
+ causes the cell at the double-click point to be selected.
+ """
+ _dbg('wxTimeCtrl::OnDoubleClick')
+ pos = self.GetInsertionPoint()
+ self.__SetCurrentCell(pos)
+ self.SetSelection(self.__cellStart, self.__cellEnd)
def __OnChar(self, event):
@@ -339,7 +424,7 @@ class wxTimeCtrl(wxTextCtrl):
sel_start, sel_to = self.GetSelection()
selection = sel_start != sel_to
if not selection:
- self.__bSelection = false # predict unselection of entire region
+ self.__bSelection = false # predict unselection of entire region
_dbg('keycode = ', key)
_dbg('pos = ', pos)
@@ -350,8 +435,8 @@ class wxTimeCtrl(wxTextCtrl):
elif key == WXK_TAB: # skip to next field if applicable:
_dbg('key == WXK_TAB')
- dict_range = self.__dictCellRange
- dict_start = self.__dictStartCellPos
+ dict_range = _dictCellRange # (for brevity)
+ dict_start = _dictStartCellPos
if event.ShiftDown(): # tabbing backwords
###(NOTE: doesn't work; wxTE_PROCESS_TAB doesn't appear to send us this event!)
@@ -398,30 +483,30 @@ class wxTimeCtrl(wxTextCtrl):
if sel_to != pos:
if sel_to - 1 == pos: # allow unselection of position
self.__bSelection = false # predict unselection of entire region
+ self.__posSelectTo = pos
event.Skip()
- if pos in self.__listStartCellPos: # can't select pass delimiters
+ if pos in _listStartCellPos: # can't select pass delimiters
_dbg(indent=0)
return
- elif pos in self.__listEndCellPos: # can't use normal selection, because position ends up
+ elif pos in _listEndCellPos: # can't use normal selection, because position ends up
# at delimeter
_dbg('set selection from', pos-1, 'to', self.__posCurrent)
- self.__bSelection = true
- self.__posSelectTo = pos
self.__posCurrent = pos-1
- self.SetSelection(self.__posCurrent, self.__posSelectTo)
+ self.SetSelection(self.__posCurrent, pos)
_dbg(indent=0)
return
- else: event.Skip() # allow selection
+ else:
+ self.__posSelectTo = sel_to - 1 # predict change in selection
+ event.Skip() # allow selection
# else... not selecting
if selection:
_dbg('sel_start=', sel_start, 'sel_to=', sel_to, '(Clearing selection)')
self.SetSelection(pos,pos) # clear selection
- self.__bSelection = false
if pos == 0: # let base ctrl handle left bound case
event.Skip()
- elif pos in self.__listStartCellPos: # skip (left) OVER the colon/space:
+ elif pos in _listStartCellPos: # skip (left) OVER the colon/space:
self.SetInsertionPoint(pos-1) # (this causes a EVT_TEXT)
self.__SetCurrentCell(pos-2) # set resulting position as "current"
else:
@@ -433,15 +518,13 @@ class wxTimeCtrl(wxTextCtrl):
_dbg('key == WXK_RIGHT')
if event.ShiftDown():
_dbg('event.ShiftDown()')
- if sel_to in self.__listDelimPos: # can't select pass delimiters
+ if sel_to in _listDelimPos: # can't select pass delimiters
_dbg(indent=0)
return
- elif pos in self.__listEndCellPos: # can't use normal selection, because position ends up
+ elif pos in _listEndCellPos: # can't use normal selection, because position ends up
# at delimeter
_dbg('set selection from', self.__posCurrent, 'to', pos+1)
- self.__bSelection = true
- self.__posSelectTo = pos+1
- self.SetSelection(self.__posCurrent, self.__posSelectTo)
+ self.SetSelection(self.__posCurrent, pos+1)
_dbg(indent=0)
return
else: event.Skip()
@@ -451,26 +534,31 @@ class wxTimeCtrl(wxTextCtrl):
_dbg('sel_start=', sel_start, 'sel_to=', sel_to, '(Clearing selection)')
pos = sel_start
self.SetSelection(pos,pos) # clear selection
- self.__bSelection = false
- if pos == self.__dictStartCellPos[self.__lastCell]+1:
+ if pos == _dictStartCellPos[self.__lastCell]+1:
_dbg(indent=0)
return # don't allow cursor past last cell
- if pos in self.__listEndCellPos: # skip (right) OVER the colon/space:
+ if pos in _listEndCellPos: # skip (right) OVER the colon/space:
self.SetInsertionPoint(pos+1) # (this causes a EVT_TEXT)
self.__SetCurrentCell(pos+2) # set resulting position
else:
self.__SetCurrentCell(pos+1) # record the new cell position after the event is finished
- self.__bSelection = false
event.Skip()
elif key in (WXK_UP, WXK_DOWN):
_dbg('key in (WXK_UP, WXK_DOWN)')
self.IncrementValue(key, pos) # increment/decrement as appropriate
- self.SetInsertionPoint(pos)
+
elif key < WXK_SPACE or key == WXK_DELETE or key > 255:
event.Skip() # non alphanumeric; process normally (Right thing to do?)
+ elif chr(key) in ['!', 'c', 'C']: # Special character; sets the value of the control to "now"
+ _dbg("key == '!'; setting time to 'now'")
+ now = wxDateTime_Now()
+ self.SetWxDateTime(now)
+ _dbg(indent=0)
+ return
+
elif chr(key) in string.digits: # let ChangeValue validate and update current position
self.ChangeValue(chr(key), pos) # handle event (and swallow it)
@@ -482,6 +570,7 @@ class wxTimeCtrl(wxTextCtrl):
return
_dbg(indent=0)
+
def IncrementValue(self, key, pos):
_dbg('wxTimeCtrl::IncrementValue', key, pos)
text = self.GetValue()
@@ -490,15 +579,16 @@ class wxTimeCtrl(wxTextCtrl):
selection = sel_start != sel_to
cell_selected = selection and sel_to -1 != pos
- dict_start = self.__dictStartCellPos
+ dict_start = _dictStartCellPos # (for brevity)
# Determine whether we should change the entire cell or just a portion of it:
- if( not selection
- or cell_selected
- or text[pos] == ' '
- or text[pos] == '9' and text[pos-1] == ' ' and key == WXK_UP
- or text[pos] == '1' and text[pos-1] == ' ' and key == WXK_DOWN
+ if( cell_selected
+ or (pos in _listStartCellPos and not selection)
+ or (text[pos] == ' ' and text[pos+1] not in ('1', '2'))
+ or (text[pos] == '9' and text[pos-1] == ' ' and key == WXK_UP)
+ or (text[pos] == '1' and text[pos-1] == ' ' and key == WXK_DOWN)
or pos >= dict_start['am_pm']):
+
_dbg(indent=1)
self.IncrementCell(key, pos)
_dbg(indent=0)
@@ -511,8 +601,8 @@ class wxTimeCtrl(wxTextCtrl):
else: digit = ' '
else:
if pos == dict_start['hour']:
- if int(text[pos + 1]) >3: mod = 2 # allow for 20-23
- else: mod = 3 # allow 00-19
+ if int(text[pos + 1]) >3: mod = 2 # allow for 20-23
+ else: mod = 3 # allow 00-19
elif pos == dict_start['hour'] + 1:
if self.__fmt24hr:
if text[pos - 1] == '2': mod = 4 # allow hours 20-23
@@ -533,13 +623,12 @@ class wxTimeCtrl(wxTextCtrl):
_dbg(indent=0)
-
def IncrementCell(self, key, pos):
_dbg('wxTimeCtrl::IncrementCell', key, pos)
self.__SetCurrentCell(pos) # determine current cell
hour, minute, second = self.__hour, self.__minute, self.__second
text = self.GetValue()
- dict_start = self.__dictStartCellPos
+ dict_start = _dictStartCellPos # (for brevity)
if key == WXK_UP: inc = 1
else: inc = -1
@@ -558,11 +647,10 @@ class wxTimeCtrl(wxTextCtrl):
newvalue = '%.2d:%.2d:%.2d' % (hour, minute, second)
self.__posCurrent = self.__cellStart
- self.__posSelectTo = self.__cellEnd
- self.__bSelection = true
_dbg(indent=1)
self.SetValue(newvalue)
_dbg(indent=0)
+ self.SetSelection(self.__cellStart, self.__cellEnd)
def ChangeValue(self, char, pos):
@@ -574,10 +662,11 @@ class wxTimeCtrl(wxTextCtrl):
self.__posSelectTo = sel_to
self.__bSelection = selection = sel_start != sel_to
cell_selected = selection and sel_to -1 != pos
+ _dbg('cell_selected =', cell_selected)
- dict_start = self.__dictStartCellPos
+ dict_start = _dictStartCellPos # (for brevity)
- if pos in self.__listDelimPos: return # don't allow change of punctuation
+ if pos in _listDelimPos: return # don't allow change of punctuation
elif( 0 < pos < dict_start['am_pm'] and char not in string.digits):
return # AM/PM not allowed in this position
@@ -588,25 +677,28 @@ class wxTimeCtrl(wxTextCtrl):
if pos == hour_start: # if at 1st position,
if self.__fmt24hr: # and using 24 hour format
- if char not in ('0', '1', '2'): # return if digit not 0,1, or 2
- return
- if cell_selected: # replace cell contents
+ if cell_selected: # replace cell contents with hour represented by digit
newtext = '%.2d' % int(char) + text[hour_start+2:]
+ elif char not in ('0', '1', '2'): # return if digit not 0,1, or 2
+ return
else: # relace current position
newtext = char + text[pos+1:]
else: # (12 hour format)
- if char not in ('1', ' '): # can only type a 1 or space
- return
- if text[pos+1] not in ('0', '1', '2'): # and then, only if other column is 0,1, or 2
- return
- if( char == ' ' # and char isn't space and
- and (cell_selected or text[pos+1] == '0')): # 2nd column is 0 or cell isn't selected
- return
- # else... ok
- if cell_selected: # replace cell contents
- newtext = '%2d' % int(char) + text[hour_start+2:]
- else: # relace current position
- newtext = char + text[pos+1:]
+ if cell_selected:
+ if char == ' ': return # can't erase entire cell
+ elif char == '0': # treat 0 as '12'
+ newtext = '12' + text[hour_start+2:]
+ else: # replace cell contents with hour represented by digit
+ newtext = '%2d' % int(char) + text[hour_start+2:]
+ else:
+ if char not in ('1', ' '): # can only type a 1 or space
+ return
+ if text[pos+1] not in ('0', '1', '2'): # and then, only if other column is 0,1, or 2
+ return
+ if char == ' ' and text[pos+1] == '0': # and char isn't space if 2nd column is 0
+ return
+ else: # ok; replace current position
+ newtext = char + text[pos+1:]
if char == ' ': self.SetInsertionPoint(pos+1) # move insert point to legal position
elif pos == hour_start+1: # if editing 2nd position of hour
@@ -634,16 +726,17 @@ class wxTimeCtrl(wxTextCtrl):
else: return # not a valid position
# update member position vars and set selection to character changed
- self.__posCurrent = pos+1
- self.__SetCurrentCell(self.__posCurrent)
+ if not cell_selected:
+ _dbg('selecting current digit')
+ self.SetSelection(self.__posCurrent, pos+1)
_dbg(indent=1)
_dbg('newtext=', newtext)
_dbg(indent=0)
self.SetValue(newtext)
- self.SetInsertionPoint(pos+1)
+ self.SetSelection(self.__posCurrent, self.__posSelectTo)
#----------------------------------------------------------------------------
-
+# Test jig for wxTimeCtrl:
if __name__ == '__main__':
import traceback
@@ -654,14 +747,16 @@ if __name__ == '__main__':
fmt24hr = 0, test_mx = 0,
style = wxTAB_TRAVERSAL ):
- self.test_mx = test_mx
wxPanel.__init__(self, parent, id, pos, size, style)
- sizer = wxBoxSizer( wxHORIZONTAL )
+ self.test_mx = test_mx
+
self.tc = wxTimeCtrl(self, 10, fmt24hr = fmt24hr)
- sizer.AddWindow( self.tc, 0, wxALIGN_CENTRE|wxLEFT|wxTOP|wxBOTTOM, 5 )
sb = wxSpinButton( self, 20, wxDefaultPosition, wxSize(-1,20), 0 )
self.tc.BindSpinButton(sb)
+
+ sizer = wxBoxSizer( wxHORIZONTAL )
+ sizer.AddWindow( self.tc, 0, wxALIGN_CENTRE|wxLEFT|wxTOP|wxBOTTOM, 5 )
sizer.AddWindow( sb, 0, wxALIGN_CENTRE|wxRIGHT|wxTOP|wxBOTTOM, 5 )
self.SetAutoLayout( true )
@@ -686,7 +781,7 @@ if __name__ == '__main__':
fmt24hr = '24' in sys.argv
test_mx = 'mx' in sys.argv
try:
- frame = wxFrame(NULL, -1, "Junk", wxPoint(20,20), wxSize(100,100) )
+ frame = wxFrame(NULL, -1, "wxTimeCtrl Test", wxPoint(20,20), wxSize(100,100) )
panel = TestPanel(frame, -1, wxPoint(-1,-1), fmt24hr=fmt24hr, test_mx = test_mx)
frame.Show(true)
except: