Python PyQt5 线程 QObject:无法为处于不同线程中的父对象创建子对象
Python PyQt5 threading QObject: Cannot create children for a parent that is in a different thread
我在 Windows 10 计算机上使用 Python 3.7.6 和 PyQt5。我正在尝试编写一个简单的应用程序,它将 运行 三个不同的过程同时在同一个 window 中的三个单独的文本框中显示输出。我试图创建一些简单的基本代码来添加,但是在使用 PyQt5 的线程模块时遇到问题。这是我的代码:
import sys, time
from threading import Thread
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QPlainTextEdit, QHBoxLayout
)
def run1():
for i in range(20):
text_1.setPlainText(text_1.toPlainText() + (f"{i}\n"))
time.sleep(0.0125)
def run2():
for i in range(20):
text_2.setPlainText(text_2.toPlainText() + (f"{i}\n"))
time.sleep(0.0125)
def run3():
for i in range(20):
text_3.setPlainText(text_3.toPlainText() + (f"{i}\n"))
time.sleep(0.0125)
app = QApplication([sys.argv])
win = QMainWindow()
text_1 = QPlainTextEdit()
text_2 = QPlainTextEdit()
text_3 = QPlainTextEdit()
my_widget = QWidget()
my_widget.layout = QHBoxLayout()
my_widget.layout.addWidget(text_1)
my_widget.layout.addWidget(text_2)
my_widget.layout.addWidget(text_3)
my_widget.setLayout(my_widget.layout)
win.setCentralWidget(my_widget)
t1 = Thread(target=run1)
t2 = Thread(target=run2)
t3 = Thread(target=run3)
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
win.show()
sys.exit(app.exec_())
当我 运行 此代码时,它显示了所需的输出,但有多个实例出现以下错误:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTextDocument(0x166abf795e0), parent's thread is QThread(0x166a9bb0fb0), current thread is QThread(0x166abf56000)
我想我知道为什么会这样,但不知道如何解决。我认为我应该使用 PyQt5 自己的 QThread class,但不知道如何去做。目前,我将 运行 我的三个独立的基于文本的 Python 应用程序,每个应用程序都在其自己的 window 中显示其输出,但我更喜欢一个基于 GUI 的应用程序,其中包含所有三个。
问题与是否使用QThread无关。问题在于 GUI 元素(例如 QWidget、QTextDocument 等)不是 thread-safe,因此您不应修改它们或在与主线程不同的线程中创建它们。为了强调我在解决方案中的最初评论,我不会使用 QThread,但我会继续使用线程,但我会通过信号将信息发送到主线程(如果它们是 thread-safe 会怎么样):
import sys, time
from threading import Thread
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QPlainTextEdit,
QHBoxLayout,
)
class Worker(QObject):
messageChanged = pyqtSignal(str)
def start(self, fn):
Thread(target=self._execute, args=(fn,), daemon=True).start()
def _execute(self, fn):
fn(self)
def write(self, message):
self.messageChanged.emit(message)
def run1(worker):
for i in range(20):
worker.write(f"{i}\n")
time.sleep(0.0125)
def run2(worker):
for i in range(20):
worker.write(f"{i}\n")
time.sleep(0.0125)
def run3(worker):
for i in range(20):
worker.write(f"{i}\n")
time.sleep(0.0125)
app = QApplication([sys.argv])
win = QMainWindow()
text_1 = QPlainTextEdit()
text_2 = QPlainTextEdit()
text_3 = QPlainTextEdit()
my_widget = QWidget()
my_widget.layout = QHBoxLayout()
my_widget.layout.addWidget(text_1)
my_widget.layout.addWidget(text_2)
my_widget.layout.addWidget(text_3)
my_widget.setLayout(my_widget.layout)
win.setCentralWidget(my_widget)
worker1 = Worker()
worker1.messageChanged.connect(text_1.appendPlainText)
worker2 = Worker()
worker2.messageChanged.connect(text_2.appendPlainText)
worker3 = Worker()
worker3.messageChanged.connect(text_3.appendPlainText)
worker1.start(run1)
worker2.start(run2)
worker3.start(run3)
win.show()
sys.exit(app.exec_())
我在 Windows 10 计算机上使用 Python 3.7.6 和 PyQt5。我正在尝试编写一个简单的应用程序,它将 运行 三个不同的过程同时在同一个 window 中的三个单独的文本框中显示输出。我试图创建一些简单的基本代码来添加,但是在使用 PyQt5 的线程模块时遇到问题。这是我的代码:
import sys, time
from threading import Thread
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QPlainTextEdit, QHBoxLayout
)
def run1():
for i in range(20):
text_1.setPlainText(text_1.toPlainText() + (f"{i}\n"))
time.sleep(0.0125)
def run2():
for i in range(20):
text_2.setPlainText(text_2.toPlainText() + (f"{i}\n"))
time.sleep(0.0125)
def run3():
for i in range(20):
text_3.setPlainText(text_3.toPlainText() + (f"{i}\n"))
time.sleep(0.0125)
app = QApplication([sys.argv])
win = QMainWindow()
text_1 = QPlainTextEdit()
text_2 = QPlainTextEdit()
text_3 = QPlainTextEdit()
my_widget = QWidget()
my_widget.layout = QHBoxLayout()
my_widget.layout.addWidget(text_1)
my_widget.layout.addWidget(text_2)
my_widget.layout.addWidget(text_3)
my_widget.setLayout(my_widget.layout)
win.setCentralWidget(my_widget)
t1 = Thread(target=run1)
t2 = Thread(target=run2)
t3 = Thread(target=run3)
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
win.show()
sys.exit(app.exec_())
当我 运行 此代码时,它显示了所需的输出,但有多个实例出现以下错误:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTextDocument(0x166abf795e0), parent's thread is QThread(0x166a9bb0fb0), current thread is QThread(0x166abf56000)
我想我知道为什么会这样,但不知道如何解决。我认为我应该使用 PyQt5 自己的 QThread class,但不知道如何去做。目前,我将 运行 我的三个独立的基于文本的 Python 应用程序,每个应用程序都在其自己的 window 中显示其输出,但我更喜欢一个基于 GUI 的应用程序,其中包含所有三个。
问题与是否使用QThread无关。问题在于 GUI 元素(例如 QWidget、QTextDocument 等)不是 thread-safe,因此您不应修改它们或在与主线程不同的线程中创建它们。为了强调我在解决方案中的最初评论,我不会使用 QThread,但我会继续使用线程,但我会通过信号将信息发送到主线程(如果它们是 thread-safe 会怎么样):
import sys, time
from threading import Thread
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QPlainTextEdit,
QHBoxLayout,
)
class Worker(QObject):
messageChanged = pyqtSignal(str)
def start(self, fn):
Thread(target=self._execute, args=(fn,), daemon=True).start()
def _execute(self, fn):
fn(self)
def write(self, message):
self.messageChanged.emit(message)
def run1(worker):
for i in range(20):
worker.write(f"{i}\n")
time.sleep(0.0125)
def run2(worker):
for i in range(20):
worker.write(f"{i}\n")
time.sleep(0.0125)
def run3(worker):
for i in range(20):
worker.write(f"{i}\n")
time.sleep(0.0125)
app = QApplication([sys.argv])
win = QMainWindow()
text_1 = QPlainTextEdit()
text_2 = QPlainTextEdit()
text_3 = QPlainTextEdit()
my_widget = QWidget()
my_widget.layout = QHBoxLayout()
my_widget.layout.addWidget(text_1)
my_widget.layout.addWidget(text_2)
my_widget.layout.addWidget(text_3)
my_widget.setLayout(my_widget.layout)
win.setCentralWidget(my_widget)
worker1 = Worker()
worker1.messageChanged.connect(text_1.appendPlainText)
worker2 = Worker()
worker2.messageChanged.connect(text_2.appendPlainText)
worker3 = Worker()
worker3.messageChanged.connect(text_3.appendPlainText)
worker1.start(run1)
worker2.start(run2)
worker3.start(run3)
win.show()
sys.exit(app.exec_())