启动 PyQt 应用程序而不阻塞主线程

问题描述 投票:0回答:1

我正在向现有应用程序添加 PyQT UI。我必须通过从主线程上的应用程序收到的回调来初始化

QAppliaction
。我无法从回调同步执行此操作,因为我最终必须调用阻塞的
app.exec()
,从而阻止现有应用程序继续运行。显然,生成常规线程不起作用,而且我似乎找不到一种方法来使用
QThread

这个问题似乎正是我在C中想要的:

thread = new QThread();
connect(thread, SIGNAL(started()), this, SLOT(onStarted()), Qt::DirectConnection);
thread->start();

其中

onStarted
最终调用
exec()
。显然我不够聪明,无法在 Python 中实现完全相同的事情。它是如何工作的?

python multithreading qt pyqt5 qthread
1个回答
0
投票

⚠️ 请注意:尽管这个答案主要针对

pyside
,但它应该与
PyQt
几乎完全相同。
(当然有一些细微的变化)

简介

两种方式,一种安全,一种稍微不安全:

  1. 您可以安全地使用
    QApplication在“主”循环之外创建
    multiprocessing
    的实例(作为不同的过程)
    ,再加上
    multiprocessing.Manager
    来处理它们之间的共享数据。
  2. 您也可以不安全地这样做,使用
    threding.thread
    并可选择抑制潜在的警告问题。

两者各有优缺点,主要是:“易用性”和安全性[...]这里我将简要演示这两种方式,最后我将附上一个实际案例我必须使用其中之一。

multiprocessing

multiprocessing
非常简单,尽管在共享引用数据时可能特别棘手。在这种情况下,您需要使用
multiprocessing.Manager

from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget
import multiprocessing
from time import sleep
import sys


class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Multiprocessing with PySide - example")
        self.setGeometry(100, 100, 300, 200)

        layout = QVBoxLayout()

        self.button = QPushButton("Click me!")
        self.button.clicked.connect(self.on_button_click)
        layout.addWidget(self.button)

        central_widget = QWidget()
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

    def on_button_click(self):
        print(f"Button clicked!")


def start_qt():
    global window
    app = QApplication(sys.argv)
    window = MyWindow()
    window.show()
    app.exec() 


if __name__ == "__main__":
    proc = multiprocessing.Process(target=start_qt, args=()) 
    proc.start()
    
    print("Main thread is free for use")
    sleep(2)
    print("Global variables are not shared...")
    sleep(2)
    print("Use `multiprocessing.Manager`")
    sleep(2)
    print("It's a new procces!")
    sleep(10)

    proc.terminate()

threding.thread

threding.thread
根据您的需求,这可能是一个不错的选择(我想在某些时候它绝对适合我......)。但是诸如警告之类的东西以及诸如 Python 线程。线程、作用域和垃圾收集 之类的东西可能对您来说不太理想

from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget
from time import sleep
import threading
import sys


class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Threading.thread with PySide - example")
        self.setGeometry(100, 100, 300, 200)

        layout = QVBoxLayout()

        self.button = QPushButton("Click me!")
        self.button.clicked.connect(self.on_button_click)
        layout.addWidget(self.button)

        central_widget = QWidget()
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

    def on_button_click(self):
        print(f"Button clicked!")


def start_qt():
    global window
    app = QApplication(sys.argv)
    window = MyWindow()
    window.show()
    app.exec() 

if __name__ == "__main__":
    t = threading.Thread(target=start_qt)
    t.daemon = True
    t.start()

    print("Main thread is free for use")
    sleep(2)
    print("Global variables are shared!")
    sleep(2)
    print("But stuff are sensitive and unsafe!")
    sleep(2)
    print("It's a new thread!")
    sleep(10)

您应该期待:

QObject::killTimer: Timers cannot be stopped from another thread
[1]    10216 segmentation fault (core dumped)  python my_script.py

这是我无法找到终止守护线程进程的正确方法的结果。要抑制此类消息,您可以使用类似的方法:

def qtMessageHandler(type: QtCore.QtMsgType, context: QtCore.QMessageLogContext, msg: str):
    pass # Handle specific messages
...
QtCore.qInstallMessageHandler(qtMessageHandler) # NOT recommended though

此外,这可能很方便:

class writerr(object):
    def write(self, data): # Ignore warning
        if not data.endswith("g_main_context_pop_thread_default: assertion 'stack != NULL' failed\n"):
            print(data)

sys.stderr = writerr()

研究

这是我找到的所有来源\链接,与以下内容相关:

尾声

请确保在实际应用此处演示的内容之前进行研究。这是我学习如何处理这个问题的过程的结果。正如所承诺的,这里+这里我必须使用这样的案例。

© www.soinside.com 2019 - 2024. All rights reserved.