PyQt의 두 번째 스레드에서 자식 대화 상자를 여는 적절한 방법은 무엇입니까?

mapf

두 번째 스레드에서 일부 프로세스를 실행하고 특정 조건이 주어지면 특정 조건이 주어지면 다른 대화 상자 창이 열리고 무언가를 확인할 때까지 프로세스가 중지되는 응용 프로그램이 있습니다. 이로 인해 다음 오류 메시지가 발생합니다.

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입니다.

이제 내 응용 프로그램에서를 사용하여 팝업되는 새 대화 상자에 대한 인터페이스를 실제로로드했는데 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_())

편집하다

나는 이미 내 문제와 관련이있을 수있는 이 게시물을 찾았다는 것을 언급하는 것을 잊었 지만, 최고 답변에서 솔루션의 추론을 이해하지 못하고 더 중요한 것은 C ++라고 믿는 것을 말하지 않는다는 것입니다. .

Eyllanesc

보조 스레드에서 GUI 요소를 만들거나 수정할 수 없으며 이는 오류 메시지로 표시됩니다.

Handler 클래스를 다시 디자인해야합니다. 요구 사항에 따라 실행을 두 가지 방법으로 나누어야합니다. 첫 번째 방법은 GUI가 대화 상자를 열고 결과를 얻고 두 번째 방법을 시작하는 최대 50 %의 진행률을 생성합니다.

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

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

Related 관련 기사

뜨겁다태그

보관