Search code examples
python-2.7eventskeyboard-shortcutswxpython

wxPython 4: detection of double keystroke


Is there a way with wxPython 4 to recognize double pressing the same key? Especially in my case, I would like to recognize when the SHIFT key is pressed twice in quick succession.


Solution

  • Here is a more general solution

    The event EVT_CHAR_HOOK is used instead of EVT_KEY_UP. The problem with EVT_KEY_UP is that it is swallowed when the event is bound to a frame that contains a panel.

    The challenge with EVT_CHAR_HOOK is to determine whether the key is actually pressed twice or only held. Therefore the RawKeyFlags are read out. The bit at position 30 indicates whether the key was held or not.

    Please note, however, that this solution only works on Windows systems!

    import wx
    
    class DoubleKeyStrokeListener(object):
        def __init__(self, parent, keyCode, callback, timeout=500):
            self._monitoredKey = keyCode
            self._callback = callback
            self._timeout = timeout
    
            self._firstPressRecognized = False
            self._keyTimer = wx.Timer(parent)
    
            parent.Bind(wx.EVT_CHAR_HOOK, self._OnKeyPressed)
            parent.Bind(wx.EVT_TIMER, self._OnTimer, self._keyTimer)
    
        def _OnKeyPressed(self, event):
            event.Skip()
    
            pressedKey = event.GetKeyCode()
    
            if pressedKey == self._monitoredKey:
                rawFlags = event.GetRawKeyFlags()
    
                # bit at position 30 is "previous key state" flag
                prevStateBit = rawFlags >> 30 & 1
    
                if prevStateBit == 1:  # -> key is held
                    return
    
                if self._firstPressRecognized:
                    self._firstPressRecognized = False
                    self._callback(event)
                else:
                    self._firstPressRecognized = True
                    self._keyTimer.StartOnce(self._timeout)
            else:
                self._firstPressRecognized = False
    
        def _OnTimer(self, event):
            self._firstPressRecognized = False
            event.Skip()