Search code examples
python-3.xpyqt5keyeventqkeyevent

PyQt5 - Double press same keyboard key


I am handling multiple key presses by subclassing QMainWindow and overriding the keyPressEvent and keyReleaseEvent functions, and then updating a dictionary if the key is down or not...

  def keyPressEvent(self,event):
    self.pressedKeys[event.key()] = True

  def keyReleaseEvent(self,event):
    self.pressedKeys[event.key()] = False

... and the pressedKeysArray being:

  pressedKeys = {Qt.Key_Left: False,
                 Qt.Key_Right: False,
                 Qt.Key_Down: False,
                 Qt.Key_Up: False}

However, I would like to be able to catch a double right arrow click, say within 100 or 200 milliseconds of each other. I would expand the pressedKeys dictionary to include double clicked keys that can be set to True or False, but I don't know how to know if the key has been pressed twice in quick succession.

Here is my code so far:

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys

class MainWindow(QMainWindow):
  pressedKeys = {Qt.Key_Left: False,
                 Qt.Key_Right: False,
                 Qt.Key_Down: False,
                 Qt.Key_Up: False}

  def __init__(self,parent=None,*args,**kwargs):
    QMainWindow.__init__(self,parent,*args,**kwargs)
    self.parent = parent

    self.timer = QTimer()
    self.timer.timeout.connect(self.keyAction)
    self.timer.start(50)

    self.show()

  def keyPressEvent(self,event):
    self.pressedKeys[event.key()] = True

  def keyReleaseEvent(self,event):
    self.pressedKeys[event.key()] = False

  def keyAction(self):
    if self.pressedKeys[Qt.Key_Left] and self.pressedKeys[Qt.Key_Down]: print("rolling left")

    elif self.pressedKeys[Qt.Key_Right] and self.pressedKeys[Qt.Key_Down]: print("rolling right")

    elif self.pressedKeys[Qt.Key_Left]: print("running left")

    elif self.pressedKeys[Qt.Key_Right]: print("running right")

    elif self.pressedKeys[Qt.Key_Down]: print("crouching") #return here to block effect of 'up' key being pressed

    if self.pressedKeys[Qt.Key_Up]: print("jumping")

def exceptHook(exectype,value,traceback):
  sys.__excepthook__(exectype,value,traceback)

if __name__ == "__main__":
  sys.excepthook = exceptHook
  app = QApplication(sys.argv)
  mainWindow = MainWindow()
  app.quit()

Solution

  • A possible solution is to keep a property last_pressed to keep track of the most recent pressed key. Instead of using the timer to call keyAction, just call keyAction from the key press and release events. Now in the keyPressEvent if the timer isActive and last_pressed == event.key(), you know it was a "double" key press. Otherwise, assign the last pressed key and start the timer. If the timer reaches timeout, reset last_pressed.

    class MainWindow(QMainWindow):
    
        pressedKeys = {Qt.Key_Left: False, Qt.Key_Right: False,
                       Qt.Key_Down: False, Qt.Key_Up: False}
    
        def __init__(self,parent=None,*args,**kwargs):
            QMainWindow.__init__(self,parent,*args,**kwargs)
            self.parent = parent
            self.last_pressed = None
    
            self.timer = QTimer()
            self.timer.setSingleShot(True)
            self.timer.timeout.connect(self.clear_pressed)
    
            self.show()
    
        def clear_pressed(self):
            self.last_pressed = None
    
        def keyPressEvent(self,event):
            self.pressedKeys[event.key()] = True
            if self.timer.isActive():
                if self.last_pressed == event.key():
                    print('Double')
                    self.timer.stop()
            else:
                self.timer.start(200)
            self.last_pressed = event.key()
            self.keyAction()
    
        def keyReleaseEvent(self,event):
            self.pressedKeys[event.key()] = False
            self.keyAction()