⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 timectrl.py

📁 Wxpython Implemented on Windows CE, Source code
💻 PY
📖 第 1 页 / 共 4 页
字号:

        # Get wxDateTime representations of bounds:
        min = self.GetMin()
        max = self.GetMax()

        midnight = wx.DateTimeFromDMY(1, 0, 1970)
        if min <= max:   # they don't span midnight
            ret = min <= value <= max

        else:
            # have to break into 2 tests; to be in bounds
            # either "min" <= value (<= midnight of *next day*)
            # or midnight <= value <= "max"
            ret = min <= value or (midnight <= value <= max)
##        dbg('in bounds?', ret, indent=0)
        return ret


    def IsValid( self, value ):
        """
        Can be used to determine if a given value would be a legal and
        in-bounds value for the control.
        """
        try:
            self.__validateValue(value)
            return True
        except ValueError:
            return False

    def SetFormat(self, format):
        self.SetParameters(format=format)

    def GetFormat(self):
        if self.__displaySeconds:
            if self.__fmt24hr: return '24HHMMSS'
            else:              return 'HHMMSS'
        else:
            if self.__fmt24hr: return '24HHMM'
            else:              return 'HHMM'

#-------------------------------------------------------------------------------------------------------------
# these are private functions and overrides:


    def __OnTextChange(self, event=None):
##        dbg('TimeCtrl::OnTextChange', indent=1)

        # Allow Maskedtext base control to color as appropriate,
        # and Skip the EVT_TEXT event (if appropriate.)
        ##! WS: For some inexplicable reason, every wxTextCtrl.SetValue()
        ## call is generating two (2) EVT_TEXT events. (!)
        ## The the only mechanism I can find to mask this problem is to
        ## keep track of last value seen, and declare a valid EVT_TEXT
        ## event iff the value has actually changed.  The masked edit
        ## OnTextChange routine does this, and returns True on a valid event,
        ## False otherwise.
        if not BaseMaskedTextCtrl._OnTextChange(self, event):
            return

##        dbg('firing TimeUpdatedEvent...')
        evt = TimeUpdatedEvent(self.GetId(), self.GetValue())
        evt.SetEventObject(self)
        self.GetEventHandler().ProcessEvent(evt)
##        dbg(indent=0)


    def SetInsertionPoint(self, pos):
        """
        This override records the specified position and associated cell before
        calling base class' function.  This is necessary to handle the optional
        spin button, because the insertion point is lost when the focus shifts
        to the spin button.
        """
##        dbg('TimeCtrl::SetInsertionPoint', pos, indent=1)
        BaseMaskedTextCtrl.SetInsertionPoint(self, pos)                 # (causes EVT_TEXT event to fire)
        self.__posCurrent = self.GetInsertionPoint()
##        dbg(indent=0)


    def SetSelection(self, sel_start, sel_to):
##        dbg('TimeCtrl::SetSelection', sel_start, sel_to, indent=1)

        # Adjust selection range to legal extent if not already
        if sel_start < 0:
            sel_start = 0

        if self.__posCurrent != sel_start:                      # force selection and insertion point to match
            self.SetInsertionPoint(sel_start)
        cell_start, cell_end = self._FindField(sel_start)._extent
        if not cell_start <= sel_to <= cell_end:
            sel_to = cell_end

        self.__bSelection = sel_start != sel_to
        BaseMaskedTextCtrl.SetSelection(self, sel_start, sel_to)
##        dbg(indent=0)


    def __OnSpin(self, key):
        """
        This is the function that gets called in response to up/down arrow or
        bound spin button events.
        """
        self.__IncrementValue(key, self.__posCurrent)   # changes the value

        # Ensure adjusted control regains focus and has adjusted portion
        # selected:
        self.SetFocus()
        start, end = self._FindField(self.__posCurrent)._extent
        self.SetInsertionPoint(start)
        self.SetSelection(start, end)
##        dbg('current position:', self.__posCurrent)


    def __OnSpinUp(self, event):
        """
        Event handler for any bound spin button on EVT_SPIN_UP;
        causes control to behave as if up arrow was pressed.
        """
##        dbg('TimeCtrl::OnSpinUp', indent=1)
        self.__OnSpin(wx.WXK_UP)
        keep_processing = False
##        dbg(indent=0)
        return keep_processing


    def __OnSpinDown(self, event):
        """
        Event handler for any bound spin button on EVT_SPIN_DOWN;
        causes control to behave as if down arrow was pressed.
        """
##        dbg('TimeCtrl::OnSpinDown', indent=1)
        self.__OnSpin(wx.WXK_DOWN)
        keep_processing = False
##        dbg(indent=0)
        return keep_processing


    def __OnChar(self, event):
        """
        Handler to explicitly look for ':' keyevents, and if found,
        clear the m_shiftDown field, so it will behave as forward tab.
        It then calls the base control's _OnChar routine with the modified
        event instance.
        """
##        dbg('TimeCtrl::OnChar', indent=1)
        keycode = event.GetKeyCode()
##        dbg('keycode:', keycode)
        if keycode == ord(':'):
##            dbg('colon seen! removing shift attribute')
            event.m_shiftDown = False
        BaseMaskedTextCtrl._OnChar(self, event )              ## handle each keypress
##        dbg(indent=0)


    def __OnSetToNow(self, event):
        """
        This is the key handler for '!' and 'c'; this allows the user to
        quickly set the value of the control to the current time.
        """
        self.SetValue(wx.DateTime_Now().FormatTime())
        keep_processing = False
        return keep_processing


    def __LimitSelection(self, event):
        """
        Event handler for motion events; this handler
        changes limits the selection to the new cell boundaries.
        """
##        dbg('TimeCtrl::LimitSelection', indent=1)
        pos = self.GetInsertionPoint()
        self.__posCurrent = pos
        sel_start, sel_to = self.GetSelection()
        selection = sel_start != sel_to
        if selection:
            # only allow selection to end of current cell:
            start, end = self._FindField(sel_start)._extent
            if sel_to < pos:   sel_to = start
            elif sel_to > pos: sel_to = end

##        dbg('new pos =', self.__posCurrent, 'select to ', sel_to)
        self.SetInsertionPoint(self.__posCurrent)
        self.SetSelection(self.__posCurrent, sel_to)
        if event: event.Skip()
##        dbg(indent=0)


    def __IncrementValue(self, key, pos):
##        dbg('TimeCtrl::IncrementValue', key, pos, indent=1)
        text = self.GetValue()
        field = self._FindField(pos)
##        dbg('field: ', field._index)
        start, end = field._extent
        slice = text[start:end]
        if key == wx.WXK_UP: increment = 1
        else:             increment = -1

        if slice in ('A', 'P'):
            if slice == 'A': newslice = 'P'
            elif slice == 'P': newslice = 'A'
            newvalue = text[:start] + newslice + text[end:]

        elif field._index == 0:
            # adjusting this field is trickier, as its value can affect the
            # am/pm setting.  So, we use wxDateTime to generate a new value for us:
            # (Use a fixed date not subject to DST variations:)
            converter = wx.DateTimeFromDMY(1, 0, 1970)
##            dbg('text: "%s"' % text)
            converter.ParseTime(text.strip())
            currenthour = converter.GetHour()
##            dbg('current hour:', currenthour)
            newhour = (currenthour + increment) % 24
##            dbg('newhour:', newhour)
            converter.SetHour(newhour)
##            dbg('converter.GetHour():', converter.GetHour())
            newvalue = converter     # take advantage of auto-conversion for am/pm in .SetValue()

        else:   # minute or second field; handled the same way:
            newslice = "%02d" % ((int(slice) + increment) % 60)
            newvalue = text[:start] + newslice + text[end:]

        try:
            self.SetValue(newvalue)

        except ValueError:  # must not be in bounds:
            if not wx.Validator_IsSilent():
                wx.Bell()
##        dbg(indent=0)


    def _toGUI( self, wxdt ):
        """
        This function takes a wxdt as an unambiguous representation of a time, and
        converts it to a string appropriate for the format of the control.
        """
        if self.__fmt24hr:
            if self.__displaySeconds: strval = wxdt.Format('%H:%M:%S')
            else:                     strval = wxdt.Format('%H:%M')
        else:
            if self.__displaySeconds: strval = wxdt.Format('%I:%M:%S %p')
            else:                     strval = wxdt.Format('%I:%M %p')

        return strval


    def __validateValue( self, value ):
        """
        This function converts the value to a wxDateTime if not already one,
        does bounds checking and raises ValueError if argument is
        not a valid value for the control as currently specified.
        It is used by both the SetValue() and the IsValid() methods.
        """
##        dbg('TimeCtrl::__validateValue(%s)' % repr(value), indent=1)
        if not value:
##            dbg(indent=0)
            raise ValueError('%s not a valid time value' % repr(value))

        valid = True    # assume true
        try:
            value = self.GetWxDateTime(value)   # regularize form; can generate ValueError if problem doing so
        except:
##            dbg('exception occurred', indent=0)
            raise

        if self.IsLimited() and not self.IsInBounds(value):
##            dbg(indent=0)
            raise ValueError (
                'value %s is not within the bounds of the control' % str(value) )
##        dbg(indent=0)
        return value

#----------------------------------------------------------------------------
# Test jig for TimeCtrl:

if __name__ == '__main__':
    import traceback

    class TestPanel(wx.Panel):
        def __init__(self, parent, id,
                     pos = wx.DefaultPosition, size = wx.DefaultSize,
                     fmt24hr = 0, test_mx = 0,
                     style = wx.TAB_TRAVERSAL ):

            wx.Panel.__init__(self, parent, id, pos, size, style)

            self.test_mx = test_mx

            self.tc = TimeCtrl(self, 10, fmt24hr = fmt24hr)
            sb = wx.SpinButton( self, 20, wx.DefaultPosition, (-1,20), 0 )
            self.tc.BindSpinButton(sb)

            sizer = wx.BoxSizer( wx.HORIZONTAL )
            sizer.Add( self.tc, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.TOP|wx.BOTTOM, 5 )
            sizer.Add( sb, 0, wx.ALIGN_CENTRE|wx.RIGHT|wx.TOP|wx.BOTTOM, 5 )

            self.SetAutoLayout( True )
            self.SetSizer( sizer )
            sizer.Fit( self )
            sizer.SetSizeHints( self )

            self.Bind(EVT_TIMEUPDATE, self.OnTimeChange, self.tc)

        def OnTimeChange(self, event):
##            dbg('OnTimeChange: value = ', event.GetValue())
            wxdt = self.tc.GetWxDateTime()
##            dbg('wxdt =', wxdt.GetHour(), wxdt.GetMinute(), wxdt.GetSecond())
            if self.test_mx:
                mxdt = self.tc.GetMxDateTime()
##                dbg('mxdt =', mxdt.hour, mxdt.minute, mxdt.second)


    class MyApp(wx.App):
        def OnInit(self):
            import sys
            fmt24hr = '24' in sys.argv
            test_mx = 'mx' in sys.argv
            try:
                frame = wx.Frame(None, -1, "TimeCtrl Test", (20,20), (100,100) )
                panel = TestPanel(frame, -1, (-1,-1), fmt24hr=fmt24hr, test_mx = test_mx)
                frame.Show(True)
            except:
                traceback.print_exc()
                return False
            return True

    try:
        app = MyApp(0)
        app.MainLoop()
    except:
        traceback.print_exc()
__i=0

## CHANGELOG:
## ====================
##  Version 1.3
##  1. Converted docstrings to reST format, added doc for ePyDoc.
##  2. Renamed helper functions, vars etc. not intended to be visible in public
##     interface to code.
##
## Version 1.2
##   1. Changed parameter name display_seconds to displaySeconds, to follow
##      other masked edit conventions.
##   2. Added format parameter, to remove need to use both fmt24hr and displaySeconds.
##   3. Changed inheritance to use BaseMaskedTextCtrl, to remove exposure of
##      nonsensical parameter methods from the control, so it will work
##      properly with Boa.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -