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

Python 在PyQt的第二个线程中打开子对话框的正确方式是什么?,python,pyqt,pyqt5,qthread,Python,Pyqt,Pyqt5,Qthread,我有一个应用程序,我在第二个线程中运行某个进程,在某个时刻,给定某个条件,另一个对话框窗口打开,该窗口将停止该进程,直到您确认某些内容。这将导致以下错误消息: QObject: Cannot create children for a parent that is in a different thread. (Parent is QApplication(0x1f9c82383d0), parent's thread is QThread(0x1f9c7ade2a0), current thr

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

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)
有趣的是,如果在进程运行时,在弹出新对话框之前,将光标移动到
main窗口上,它还会多次生成此错误消息:

QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
很奇怪。因为只有当您将光标移动到
main窗口上时才会发生此错误

现在,在我的应用程序中,我实际上为使用
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_())
导入系统 导入时间 将numpy作为np导入 从PyQt5.QtCore导入QObject、pyqtSignal、QThread 从PyQt5.QtWidgets导入( QDialog、QApplication、QPushButton、QGridLayout、QProgressBar、QLabel ) 课堂专题对话(QDialog): 定义初始化(自): super()。\uuuu init\uuuuu() btn=QPushButton('传递变量') btn.clicked.connect(self.accept) 布局=QGridLayout() layout.addWidget(btn) #self.setLayout(布局) self.variable=np.random.randint(01100) 类处理程序(QObject): 进度=pyqtSignal(int) 完成=pyqtSignal(int) 定义初始化(自): super()。\uuuu init\uuuuu() self.\u isRunning=True 自我成功=错误 def运行(自): 结果=无 i=0 当我<100并且自己在运行时: 如果i==50: dialog=SpecialDialog() dialog.exec() 结果=dialog.variable 睡眠时间(0.01) i+=1 self.progress.emit(一) 如果i==100: 自我成功=真实 self.finished.emit(结果) def停止(自): self.\u isRunning=False 类主窗口(QDialog): 定义初始化(自): super()。\uuuu init\uuuuu() btn=QPushButton(“测试”) btn.点击.connect(self.run\u测试) self.pbar=QProgressBar() self.resultLabel=QLabel('Result:') 布局=QGridLayout(自身) layout.addWidget(btn) layout.addWidget(self.pbar) layout.addWidget(self.resultLabel) self.setLayout(布局) self.handler=None self.handler_thread=QThread() self.result=None def运行测试(自): self.handler=handler() self.handler.moveToThread(self.handler\u线程) self.handler.progress.connect(self.progress) self.handler.finished.connect(self.finisher) self.handler\u thread.started.connect(self.handler.run) self.handler_thread.start() def进度(自我,val): self.pbar.setValue(val) def整理器(自身、结果): self.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\u线程.started.disconnect(self.handler.run) self.handler_thread.terminate() self.handler=None 如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu': app=QApplication(sys.argv) GUI=MainWindow() GUI.show() sys.exit(app.exec_())
编辑


<>我忘了提到我已经发现了,这可能与我的问题有关,但是,我不理解顶部答案中的答案的推理,更重要的是,我不说我认为是C++的。

< p>你不能从次要线程创建或修改GUI元素,这是错误消息发出的信号。 您必须重新设计处理程序类,根据您的需求,您必须将run分为2种方法,第一种方法将生成高达50%的进度,其中GUI将打开对话框,获得结果并启动第二种方法

导入系统 导入时间 将numpy作为np导入 从functools导入部分 从PyQt5.QtCore导入QObject、pyqtSignal、pyqtlot、QThread、QTimer 从PyQt5.QtWidgets导入( QDialog, QApplication, QPushButton, QGridLayout, QProgressBar, QLabel, ) 课堂专题对话(QDialog): 定义初始化(自): super()。\uuuu init\uuuuu() btn=QPushButton(“传递变量”) btn.clicked.connect(self.accept) 布局=QGridLayout() layout.addWidget(btn) #self.setLayout(布局) self.variable=np.random.randint(01100) 类处理程序(QObject): 进度=pyqtSignal(int) 完成=pyqtSignal(int) 定义初始化(自): super()。\uuuu init\uuuuu() self.\u isRunning=True 自我成功=错误 @pyqtSlot() def任务1(自我): i=0
谢谢你的回答!这使我走上了我希望的正确道路。我想我把这个例子做得有点太简单了,因为我实际上不知道什么时候满足打开新对话框的条件(如果有的话),以及这种情况发生的频率。但是,对于你的解决方案,我提出了自己的解决方案,尽管我不确定这有多好。如果你能看一下我下面的答案,那就太好了。@mapf 1)我只根据他们提供的代码解决他们提出的问题,所以我不假设复杂性或修改。如果你提供了一个更好的MRE,我会为你真正的pr提出一个更合适的解决方案