为了账号安全,请及时绑定邮箱和手机立即绑定

使用“键盘”在失去焦点时打开新窗口

使用“键盘”在失去焦点时打开新窗口

慕森卡 2022-12-20 15:27:22
我正在尝试使用模块“键盘”跟踪我的按键,而 PySide2 小部件未处于焦点状态,效果很好。但是,当我尝试使用“键盘”快捷方式创建新的 Widget 时,程序崩溃了。按下按钮打开一个窗口工作正常。我也可以使用“键盘”调用非 UI 函数,例如。打印功能没有任何问题。你知道解决这个问题的方法吗?使用“键盘”或任何其他方法打开一个新窗口,而 PySide2 窗口不在焦点上。在这个例子中,我想在“CTRL+D”上打开一个新窗口。PySide2 和 PyQt5 都存在该问题。这是我的缩短代码:import sysimport jsonimport osimport keyboardfrom PySide2.QtWidgets import QApplication, QWidget, QGridLayout, QKeySequenceEdit, QLabel, QPushButton, QShortcutfrom PySide2.QtCore import Qt, QObject, Signal, Slot # Qt.Key_W beispielsweise#from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QKeySequenceEdit, QLabel, QPushButton, QShortcut#from PyQt5.QtCore import Qt, QObject, pyqtSignal as Signal, pyqtSlot as Slot # Qt.Key_W beispielsweiseclass ConfigWindow(QWidget):    def __init__(self):        super().__init__()        self.initUi()        self.init_shortcuts()        self.show()    def initUi(self):        self.setGeometry(300,300, 400, 250)        self.setWindowTitle("Settings")        grid = QGridLayout()        self.setLayout(grid)        self.keyseq = QKeySequenceEdit("CTRL+D")        grid.addWidget(self.keyseq, 0, 0)        s_button = QPushButton("Safe")        grid.addWidget(s_button, 1, 0)        cl_button = QPushButton("Close")        grid.addWidget(cl_button, 1, 1)        cl_button.clicked.connect(self.close)        open_button = QPushButton("openw")        grid.addWidget(open_button, 2, 0)        open_button.clicked.connect(self.call_item_parser)    def keyPressEvent(self, event): #event:PySide2.QtGui.QKeyEvent        if event.key() == Qt.Key_Escape:            self.close()
查看完整描述

1 回答

?
蓝山帝景

TA贡献1843条经验 获得超7个赞

问题是因为在键盘上注册的回调是在辅助线程中执行的,可以通过修改以下部分代码并打印来验证threading.current_thread()。在 Qt 中,禁止在另一个线程中创建任何小部件,因为它们不是线程安全的。


def call_item_parser(self):

    print(threading.current_thread())

    self.h_w = ParseWindow()

    self.h_w.setWindowTitle("New Window")

    self.h_w.setGeometry(100, 100, 100, 100)

    self.h_w.show()

print(threading.current_thread())

app = QApplication(sys.argv)

w = ConfigWindow()

sys.exit(app.exec_())

输出:


<_MainThread(MainThread, started 140144979916608)>

Binding _price_keyseq to ctrl+a

<Thread(Thread-10, started daemon 140144220817152)>

一种可能的解决方案是使用信号将信息发送到主线程,并在主线程中调用回调。


import sys

from functools import partial

import platform

import threading


import keyboard



from PySide2.QtCore import Qt, QObject, Signal, Slot

from PySide2.QtGui import QKeySequence

from PySide2.QtWidgets import (

    QApplication,

    QWidget,

    QGridLayout,

    QKeySequenceEdit,

    QPushButton,

)



class KeyBoardManager(QObject):

    activated = Signal(str)


    def __init__(self, parent=None):

        super().__init__(parent)

        self._callbacks = dict()

        self.activated.connect(self._handle_activated)


    @property

    def callbacks(self):

        return self._callbacks


    def register(self, shortcut, callback, *, args=(), kwargs=None):

        self.callbacks[shortcut] = (callback, args, kwargs or {})

        keyboard.add_hotkey(shortcut, partial(self.activated.emit, shortcut))


    @Slot(str)

    def _handle_activated(self, shortcut):

        values = self.callbacks.get(shortcut)

        if values is not None:

            callback, args, kwargs = self._callbacks[shortcut]


            callback(*args, **kwargs)



class ConfigWindow(QWidget):

    def __init__(self):

        super().__init__()

        self.initUi()

        self.init_shortcuts()

        self.show()


    def initUi(self):

        self.setGeometry(300, 300, 400, 250)

        self.setWindowTitle("Settings")

        grid = QGridLayout(self)


        self.keyseq = QKeySequenceEdit("CTRL+A")

        grid.addWidget(self.keyseq, 0, 0)


        s_button = QPushButton("Safe")

        grid.addWidget(s_button, 1, 0)


        cl_button = QPushButton("Close")

        grid.addWidget(cl_button, 1, 1)

        cl_button.clicked.connect(self.close)


        open_button = QPushButton("openw")

        grid.addWidget(open_button, 2, 0)

        open_button.clicked.connect(self.call_item_parser)


    def keyPressEvent(self, event):  # event:PySide2.QtGui.QKeyEvent

        if event.key() == Qt.Key_Escape:

            self.close()


    # shortcuts are listened to, while program is running

    def init_shortcuts(self):

        self.keyboard_manager = KeyBoardManager()


        str_value = self.keyseq.keySequence().toString()

        if platform.system() == "Linux":

            str_value = str_value.lower()

        print("Binding _price_keyseq to {}".format(str_value))

        self.keyboard_manager.register(str_value, self.call_item_parser)


    def call_item_parser(self):

        print(threading.current_thread())

        self.h_w = ParseWindow()

        self.h_w.setWindowTitle("New Window")

        self.h_w.setGeometry(100, 100, 100, 100)

        self.h_w.show()



class ParseWindow(QWidget):

    pass



def main():

    print(threading.current_thread())

    app = QApplication(sys.argv)

    w = ConfigWindow()

    sys.exit(app.exec_())



if __name__ == "__main__":

    main()

输出:


<_MainThread(MainThread, started 140037641176896)>

Binding _price_keyseq to ctrl+a

<_MainThread(MainThread, started 140037641176896)>


查看完整回答
反对 回复 2022-12-20
  • 1 回答
  • 0 关注
  • 114 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号