PyQt5使用pynput全局监听O键创建新窗口时Widget崩溃问题求解
问题原因
pynput的键盘监听器运行在独立的工作线程,而PyQt所有UI控件的创建、修改、显示操作都只允许在主线程(也就是运行QApplication事件循环的线程)执行,直接在监听器回调里创建QWidget属于跨线程操作UI,会触发未定义行为导致崩溃。
解决方案
使用Qt自带的线程安全的信号槽机制中转事件:工作线程监听到按键后只发射信号,主线程接收信号后再执行UI创建逻辑。
修复后完整代码
from PyQt5 import QtWidgets from PyQt5.QtWidgets import * from PyQt5.QtGui import * from PyQt5.QtCore import * from PyQt5 import QtCore from PyQt5.uic import loadUiType import sys from pynput import keyboard class win(QWidget): def __init__(self): super().__init__() self.initUI() # self.setWindowFlag(Qt.FramelessWindowHint) self.btn = QPushButton('close', self) self.btn.clicked.connect(lambda: self.close()) def initUI(self): self.setGeometry(300, 300, 300, 200) self.setWindowTitle('Student log in screen') self.show() class Main(QMainWindow): # 自定义信号,传递按下的按键名称 key_pressed = pyqtSignal(str) def __init__(self, parent=None): super(Main, self).__init__(parent) self.setGeometry(500, 500, 500, 400) self.start_btn = QPushButton('start', self) self.start_btn.clicked.connect(lambda : self.start()) # 绑定信号到创建窗口的槽函数,槽函数默认运行在主线程 self.key_pressed.connect(self.create_new_window) # 可选:如果需要保留多个窗口,用列表存储窗口实例,避免被回收 # self.widget_list = [] def copy(self): pya.hotkey('ctrl', 'c') time.sleep(.05) return pyperclip.paste() def create_new_window(self, key): if key == 'o': print('you pressed o') self.widget = win() # 多窗口场景替换为下面两行 # new_win = win() # self.widget_list.append(new_win) def test(self, key): try: k = key.char # single-char keys except: k = key.name # other keys # 工作线程只做信号发射,不触碰UI逻辑 self.key_pressed.emit(k) def start(self): self.listener = keyboard.Listener(on_press=self.test) self.listener.start() if __name__ == '__main__': app = QApplication(sys.argv) ex = Main() ex.show() sys.exit(app.exec_())
内容的提问来源于stack exchange,提问作者user15896474
相关产品推荐
相关产品推荐

