wxTimeCtrl updates from Will Sadkin
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_4_BRANCH@17369 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -1,54 +1,114 @@
|
||||
from wxPython.wx import *
|
||||
from wxPython.lib.timectrl import *
|
||||
|
||||
import string
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class TestPanel( wxPanel ):
|
||||
def __init__( self, parent, log ):
|
||||
|
||||
wxPanel.__init__( self, parent, -1 )
|
||||
self.log = log
|
||||
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 )
|
||||
|
||||
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 )
|
||||
|
||||
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(grid)
|
||||
grid.Fit(panel)
|
||||
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.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() ) )
|
||||
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 )
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
@@ -58,21 +118,22 @@ def runTest(frame, nb, log):
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
# 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 = """<html><body>
|
||||
<P>
|
||||
<B>wxTimeCtrl</B> 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.
|
||||
<B>wxTimeCtrl</B> 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.
|
||||
<P>
|
||||
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.
|
||||
<P>
|
||||
Here's the interface for wxTimeCtrl:
|
||||
<DL>
|
||||
|
||||
<PRE>
|
||||
<B>wxTimeCtrl</B>(parent, id = -1,
|
||||
The <B>!</B> key sets the value of the control to <B><I>now!</I></B>
|
||||
<P>
|
||||
Here's the API for wxTimeCtrl:
|
||||
<DL><PRE>
|
||||
<B>wxTimeCtrl</B>(
|
||||
parent, id = -1,
|
||||
<B>value</B> = '12:00:00 AM',
|
||||
pos = wxDefaultPosition,
|
||||
size = wxDefaultSize,
|
||||
@@ -82,36 +143,71 @@ Here's the interface for wxTimeCtrl:
|
||||
name = "time")
|
||||
</PRE>
|
||||
<UL>
|
||||
<DT><B>value</B><DD>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.)
|
||||
<DL><B>size</B><DD>The size of the control will be automatically adjusted for 12/24 hour format if wxDefaultSize is specified.
|
||||
<DT><B>fmt24hr</B><DD>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.
|
||||
<DT><B>spinButton</B><DD>If specified, this button's events will be bound to the behavior of the wxTimeCtrl, working like up/down cursor key events. (See BindSpinButton.)
|
||||
<DT><B>style</B><DD>By default, wxTimeCtrl will process TAB events, by allowing tab to the different cells within the control.
|
||||
<DT><B>value</B>
|
||||
<DD>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.)
|
||||
<DL><B>size</B>
|
||||
<DD>The size of the control will be automatically adjusted for 12/24 hour format
|
||||
if wxDefaultSize is specified.
|
||||
<BR>
|
||||
<DT><B>fmt24hr</B>
|
||||
<DD>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.
|
||||
<BR>
|
||||
<DT><B>spinButton</B>
|
||||
<DD>If specified, this button's events will be bound to the behavior of the
|
||||
wxTimeCtrl, working like up/down cursor key events. (See BindSpinButton.)
|
||||
<BR>
|
||||
<DT><B>style</B>
|
||||
<DD>By default, wxTimeCtrl will process TAB events, by allowing tab to the
|
||||
different cells within the control.
|
||||
</DL>
|
||||
</UL>
|
||||
<DT><B>SetValue(time_string)</B><DD>Sets the value of the control to a particular time, given a valid time string; raises ValueError on invalid value
|
||||
<DT><B>GetValue()</B><DD>Retrieves the string value of the time from the control
|
||||
<BR>
|
||||
<DT><B>SetWxDateTime(wxDateTime)</B><DD>Uses the time portion of a wxDateTime to construct a value for the control.
|
||||
<DT><B>GetWxDateTime()</B><DD>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".)
|
||||
<BR>
|
||||
<DT><B>SetMxDateTime(mxDateTime)</B><DD>Uses the time portion of an mxDateTime to construct a value for the control. <EM>NOTE:</EM> This imports mx.DateTime at runtime only if this or the Get function is called.
|
||||
<DT><B>GetMxDateTime()</B><DD>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.)
|
||||
<DT><B>SetValue(time_string)</B>
|
||||
<DD>Sets the value of the control to a particular time, given a valid time string;
|
||||
raises ValueError on invalid value
|
||||
<BR>
|
||||
<DT><B>BindSpinButton(wxSpinBtton)</B><DD>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.)
|
||||
<DT><B>GetValue()</B>
|
||||
<DD>Retrieves the string value of the time from the control
|
||||
<BR>
|
||||
<DT><B>EVT_TIMEUPDATE(win, id, func)</B><DD>func is fired whenever the value of the control changes.
|
||||
<DT><B>SetWxDateTime(wxDateTime)</B>
|
||||
<DD>Uses the time portion of a wxDateTime to construct a value for the control.
|
||||
<BR>
|
||||
<DT><B>GetWxDateTime()</B>
|
||||
<DD>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".)
|
||||
<BR>
|
||||
<DT><B>SetMxDateTime(mxDateTime)</B>
|
||||
<DD>Uses the time portion of an mxDateTime to construct a value for the control.
|
||||
<EM>NOTE:</EM> This imports mx.DateTime at runtime only if this or the Get function
|
||||
is called.
|
||||
<BR>
|
||||
<DT><B>GetMxDateTime()</B>
|
||||
<DD>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.)
|
||||
<BR>
|
||||
<DT><B>BindSpinButton(wxSpinBtton)</B>
|
||||
<DD>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.)
|
||||
<BR>
|
||||
<DT><B>EVT_TIMEUPDATE(win, id, func)</B>
|
||||
<DD>func is fired whenever the value of the control changes.
|
||||
</DL>
|
||||
</body></html>
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys,os
|
||||
import run
|
||||
run.main(['', os.path.basename(sys.argv[0])])
|
||||
|
||||
|
||||
|
||||
|
@@ -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,10 +27,10 @@ import string
|
||||
_debug = 0
|
||||
_indent = 0
|
||||
|
||||
if _debug:
|
||||
def _dbg(*args, **kwargs):
|
||||
global _indent
|
||||
|
||||
if _debug:
|
||||
if len(args):
|
||||
if _indent: print ' ' * 3 * _indent,
|
||||
for arg in args: print arg,
|
||||
@@ -41,7 +42,9 @@ def _dbg(*args, **kwargs):
|
||||
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 "<wxTimeCtrl: %s>" % 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):
|
||||
@@ -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)
|
||||
@@ -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,24 +677,27 @@ 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 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 char isn't space and
|
||||
and (cell_selected or text[pos+1] == '0')): # 2nd column is 0 or cell isn't selected
|
||||
if char == ' ' and text[pos+1] == '0': # and char isn't space if 2nd column is 0
|
||||
return
|
||||
# else... ok
|
||||
if cell_selected: # replace cell contents
|
||||
newtext = '%2d' % int(char) + text[hour_start+2:]
|
||||
else: # relace current position
|
||||
else: # ok; replace current position
|
||||
newtext = char + text[pos+1:]
|
||||
if char == ' ': self.SetInsertionPoint(pos+1) # move insert point to legal position
|
||||
|
||||
@@ -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:
|
||||
|
Reference in New Issue
Block a user