You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PyQt5中捕获当前所有按下的键盘按键?

解决PyQt5捕获持续按下按键的问题

问题描述

尝试用PyQt5编写GUI程序捕获当前所有按下的键盘按键,编写了两段代码:

代码1:使用事件过滤器

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_())

代码2:重写按键事件方法

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_())

运行后遇到的问题:

  • 按住单个按键时,输出列表会在包含该键值和空列表之间来回切换;
  • 按住多个按键时,最后按下的键会反复被添加和移除,看起来按住按键时持续触发了KeyRelease事件。

解决方案

问题根源是系统的按键自动重复机制:当按住按键不放时,系统会持续发送KeyPress事件(自动重复),而Qt会将这些自动重复的事件标记为isAutoRepeat() == True。如果不区分这些自动重复事件,会导致错误地处理按键状态。

修改代码,在处理按键事件前先判断是否为自动重复事件,若是则跳过处理:

修改后的事件过滤器版本

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.isAutoRepeat():
            return super().eventFilter(source, event)
            
        if event.type() == QEvent.KeyPress:
            key = int(event.key())
            if key not in self.pressedKeys:
                self.pressedKeys.append(key)
                print(self.pressedKeys)
        elif event.type() == QEvent.KeyRelease:
            key = int(event.key())
            if key in self.pressedKeys:
                self.pressedKeys.remove(key)
                print(self.pressedKeys)

        return super().eventFilter(source, event)


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

    demo = MainWindow()
    demo.show()

    sys.exit(app.exec_())

修改后的重写事件方法版本

import sys

from PyQt5.QtWidgets import QApplication, QWidget

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

        self.pressedKeys = []

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

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

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

    demo = MainWindow()
    demo.show()

    sys.exit(app.exec_())

注意事项

  • 上述代码仅能捕获当前GUI程序处于激活状态时的按键事件,如果需要捕获全局按键(即使程序后台运行),PyQt5本身无法直接实现,此时可以考虑使用专门的全局键盘监听库,比如pynput。

内容的提问来源于stack exchange,提问作者Matt Pitkin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 09:35:34