使用“键盘"在失焦时打开新窗口 [英] Opening new window while out of focus using "keyboard"

查看:26
本文介绍了使用“键盘"在失焦时打开新窗口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用模块键盘"跟踪我的按键,而 PySide2 小部件未处于焦点状态,这工作正常.但是,当我尝试使用键盘"快捷方式创建新的 Widget 时,程序崩溃了.按下按钮打开一个窗口工作正常.我也可以使用键盘"调用非 UI 函数,例如.打印功能没有任何问题.

I am trying to track my keypresses, using the module "keyboard" while a PySide2 Widget is not in focus, which works fine. However when I try to create a new Widget using a "keyboard" shortcut the program crashes. Opening a window on a button press works fine. I can also call non UI functions using "keyboard" eg. the print function without any problem.

当 PySide2 窗口不在焦点上时,您是否知道解决此问题并使用键盘"或任何其他方法打开一个新窗口的方法.在这个例子中,我想在CTRL+D"上打开一个新窗口.PySide2 和 PyQt5 都存在问题.

Do you know a way to fix this and open a new window using "keyboard" or any other method, while a PySide2 window is not in focus. In this example I want to open a new window on "CTRL+D". The Problem exists both for PySide2 and PyQt5.

这是我缩短的代码:

import sys
import json
import os
import keyboard
from PySide2.QtWidgets import QApplication, QWidget, QGridLayout, QKeySequenceEdit, QLabel, QPushButton, QShortcut
from 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 beispielsweise


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

    # shortcuts are listened to, while program is running
    def init_shortcuts(self):
        str_value = self.keyseq.keySequence().toString()
        print("Binding _price_keyseq to {}".format(str_value))
        keyboard.add_hotkey(str_value, self.call_item_parser)
        # keyboard.add_hotkey(str_value, print, args=("this works")) # this would work


    def call_item_parser(self):
        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):
    def __init__(self):
        super().__init__()


app = QApplication(sys.argv)
w = ConfigWindow()
sys.exit(app.exec_())

推荐答案

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

The problem is caused because the callback registered in keyboard is executed in a secondary thread as can be verified by modifying the following part of the code and printing threading.current_thread(). In Qt it is forbidden to create any widget in another thread since they are not thread-safe.

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

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

One possible solution is to use a signal to send the information to the main thread, and invoke the callback in the main thread.

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

这篇关于使用“键盘"在失焦时打开新窗口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆