0

I'm trying to write a PyQt5 GUI that captures all keyboard keys that are currently being pressed. Based on this answer, I've tried the following minimal code:

import sys

from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import QEvent

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        QApplication.instance().installEventFilter(self)

        self.pressedKeys = []

    def eventFilter(self, source, event):
        if event.type() == QEvent.KeyPress:
            if int(event.key()) not in self.pressedKeys:
                self.pressedKeys.append(int(event.key()))
                print(self.pressedKeys)
        elif event.type() == QEvent.KeyRelease:
            if int(event.key()) in self.pressedKeys:
                self.pressedKeys.remove(int(event.key()))
                print(self.pressedKeys)

        return super().eventFilter(source, event)


if __name__ == "__main__":
    app = QApplication(sys.argv)

    demo = MainWindow()
    demo.show()

    sys.exit(app.exec_())

When I run this, if I hold down a key the output list keeps flipping back and forth between one containing the key value and being empty. Similarly, holding down multiple keys adds the keys to the list, but alternates back and forth between containing and removing the final key that I have pressed. It seems that if I hold down keys the KeyRelease event still keeps getting triggered for the last key I pressed.

Is there are way to hold all current key presses in PyQt5, or should I use a different package (e.g., using one or other of the packages suggested in this question)?

Note, I've also tried:

import sys

from PyQt5.QtWidgets import QApplication, QWidget

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.pressedKeys = []

    def keyPressEvent(self, event):
        if int(event.key()) not in self.pressedKeys:
            self.pressedKeys.append(int(event.key()))
            print(self.pressedKeys)

    def keyReleaseEvent(self, event):
        if int(event.key()) in self.pressedKeys:
            self.pressedKeys.remove(int(event.key()))
            print(self.pressedKeys)

if __name__ == "__main__":
    app = QApplication(sys.argv)

    demo = MainWindow()
    demo.show()

    sys.exit(app.exec_())

which results in pretty much the same behaviour.

Matt Pitkin
  • 3,989
  • 1
  • 18
  • 32

1 Answers1

1

Physical keyboards have the "autorepeat" feature, which allows simulating multiple pressure of a key while keeping it pressed.

This results in having multiple press/release events while a standard key (usually, not modifiers) is down and until it's physically released, with a rate normally set for the system.

You can check if the event is actually a physical press/release or if it isAutoRepeat():

    def eventFilter(self, source, event):
        if event.type() == QEvent.KeyPress:
            if not event.isAutoRepeat() and int(event.key()) not in self.pressedKeys:
                self.pressedKeys.append(int(event.key()))
                print(self.pressedKeys)
        elif event.type() == QEvent.KeyRelease:
            if not event.isAutoRepeat() and int(event.key()) in self.pressedKeys:
                self.pressedKeys.remove(int(event.key()))
                print(self.pressedKeys)

        return super().eventFilter(source, event)
musicamante
  • 41,230
  • 6
  • 33
  • 58
  • Thanks very much. This works, but seems to max out at 4 keys pressed after which it doesn't hold any more. Do you think this might be a limitation of my keyboard/keyboard driver itself or PyQt related? – Matt Pitkin Jan 09 '23 at 18:15
  • 1
    This is a hardware limitation (see [key rollover](https://en.wikipedia.org/wiki/Key_rollover)) that depends on the keyboard, and there's nothing you can do about it: each keyboard has its own limits, and it usually it's a combination of the keyboard matrix and circuit complexity, but unless you have a high-end keyboard, you normally cannot get more than 4-8 keys. – musicamante Jan 09 '23 at 18:29