在PyQt的第二个线程中打开子对话框的正确方法是什么?

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

我有一个应用程序,在其中我在第二个线程中运行某些进程,并在一定条件下在某个点打开另一个对话框窗口,该对话框将暂停该进程,直到您确认某些内容为止。这将导致以下错误消息:

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QApplication(0x1f9c82383d0), parent's thread is QThread(0x1f9c7ade2a0), current thread is QThread(0x1f9c8358800)

有趣的是,如果您还在进程运行时并且在弹出新对话框之前将光标移到MainWindow上,它还会多次产生此错误消息:

QBasicTimer::stop: Failed. Possibly trying to stop from a different thread

非常奇怪。因为如果将光标移到MainWindow上,则会发生only

现在,在我的应用程序中,我实际上为使用PyQt5.uic.loadUi弹出的新对话框加载了一个界面,这没有引起任何问题。但是,当我为本文创建示例时,由于我在初始化新对话框时设置其布局,因此又发生了另一个问题:

QObject::setParent: Cannot set parent, new parent is in a different thread

导致应用程序崩溃的原因:

Process finished with exit code -1073741819 (0xC0000005)

我显然在这里就我猜想的线程做错了,但是我不知道是什么。我感到特别困惑的是,在使用loadUi完全可以的同时,我无法在初始化期间设置新对话框的布局。这是我的示例代码:

import sys
import time
import numpy as np

from PyQt5.QtCore import QObject, pyqtSignal, QThread
from PyQt5.QtWidgets import (
    QDialog, QApplication, QPushButton, QGridLayout, QProgressBar, QLabel
)


class SpecialDialog(QDialog):
    def __init__(self):
        super().__init__()
        btn = QPushButton('pass variable')
        btn.clicked.connect(self.accept)
        layout = QGridLayout()
        layout.addWidget(btn)
        # self.setLayout(layout)
        self.variable = np.random.randint(0, 100)


class Handler(QObject):
    progress = pyqtSignal(int)
    finished = pyqtSignal(int)

    def __init__(self):
        super().__init__()
        self._isRunning = True
        self._success = False

    def run(self):
        result = None
        i = 0
        while i < 100 and self._isRunning:
            if i == 50:
                dialog = SpecialDialog()
                dialog.exec_()
                result = dialog.variable
            time.sleep(0.01)
            i += 1
            self.progress.emit(i)

        if i == 100:
            self._success = True
            self.finished.emit(result)

    def stop(self):
        self._isRunning = False


class MainWindow(QDialog):
    def __init__(self):
        super().__init__()
        btn = QPushButton('test')
        btn.clicked.connect(self.run_test)
        self.pbar = QProgressBar()
        self.resultLabel = QLabel('Result:')
        layout = QGridLayout(self)
        layout.addWidget(btn)
        layout.addWidget(self.pbar)
        layout.addWidget(self.resultLabel)
        self.setLayout(layout)

        self.handler = None
        self.handler_thread = QThread()
        self.result = None

    def run_test(self):
        self.handler = Handler()
        self.handler.moveToThread(self.handler_thread)
        self.handler.progress.connect(self.progress)
        self.handler.finished.connect(self.finisher)
        self.handler_thread.started.connect(self.handler.run)
        self.handler_thread.start()

    def progress(self, val):
        self.pbar.setValue(val)

    def finisher(self, result):
        self.result = result
        self.resultLabel.setText(f'Result: {result}')
        self.pbar.setValue(0)
        self.handler.stop()
        self.handler.progress.disconnect(self.progress)
        self.handler.finished.disconnect(self.finisher)
        self.handler_thread.started.disconnect(self.handler.run)
        self.handler_thread.terminate()
        self.handler = None


if __name__ == '__main__':
    app = QApplication(sys.argv)
    GUI = MainWindow()
    GUI.show()
    sys.exit(app.exec_())

编辑

[我忘了提到我已经找到this post了,这可能与我的问题有关,但是,我并不理解最佳答案中该解决方案的原因,更重要的是,我不会说我所讲的相信是C ++。

python pyqt pyqt5 qthread
1个回答
0
投票

您无法从辅助线程创建或修改GUI元素,并且错误消息对此进行了指示。

您必须重新设计Handler类,根据您的要求,您必须将run划分为2个方法,第一个方法将生成多达50%的进度,其中GUI将打开对话框,获取结果并启动第二个方法。

import sys
import time
import numpy as np
from functools import partial

from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, QThread, QTimer
from PyQt5.QtWidgets import (
    QDialog,
    QApplication,
    QPushButton,
    QGridLayout,
    QProgressBar,
    QLabel,
)


class SpecialDialog(QDialog):
    def __init__(self):
        super().__init__()
        btn = QPushButton("pass variable")
        btn.clicked.connect(self.accept)
        layout = QGridLayout()
        layout.addWidget(btn)
        # self.setLayout(layout)
        self.variable = np.random.randint(0, 100)


class Handler(QObject):
    progress = pyqtSignal(int)
    finished = pyqtSignal(int)

    def __init__(self):
        super().__init__()
        self._isRunning = True
        self._success = False

    @pyqtSlot()
    def task1(self):
        i = 0
        while i <= 50 and self._isRunning:
            time.sleep(0.01)
            i += 1
            self.progress.emit(i)

    @pyqtSlot(int)
    def task2(self, result):
        i = 50
        while i < 100 and self._isRunning:
            time.sleep(0.01)
            i += 1
            self.progress.emit(i)

        if i == 100:
            self._success = True
            self.finished.emit(result)

    def stop(self):
        self._isRunning = False


class MainWindow(QDialog):
    def __init__(self):
        super().__init__()
        btn = QPushButton("test")
        btn.clicked.connect(self.run_test)
        self.pbar = QProgressBar()
        self.resultLabel = QLabel("Result:")
        layout = QGridLayout(self)
        layout.addWidget(btn)
        layout.addWidget(self.pbar)
        layout.addWidget(self.resultLabel)
        self.setLayout(layout)

        self.handler = None
        self.handler_thread = QThread()
        self.result = None

    def run_test(self):
        self.handler = Handler()
        self.handler.moveToThread(self.handler_thread)
        self.handler.progress.connect(self.progress)
        self.handler.finished.connect(self.finisher)
        self.handler_thread.started.connect(self.handler.task1)
        self.handler_thread.start()

    @pyqtSlot(int)
    def progress(self, val):
        self.pbar.setValue(val)
        if val == 50:
            dialog = SpecialDialog()
            dialog.exec_()
            result = dialog.variable
            wrapper = partial(self.handler.task2, result)
            QTimer.singleShot(0, wrapper)

    def finisher(self, result):
        self.result = result
        self.resultLabel.setText(f"Result: {result}")
        self.pbar.setValue(0)
        self.handler.stop()
        self.handler_thread.quit()
        self.handler_thread.wait()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    GUI = MainWindow()
    GUI.show()
    sys.exit(app.exec_())
© www.soinside.com 2019 - 2024. All rights reserved.