I created a GUI in PyQT5 which consisted of 2 QPlainTextEdit widgets. Each QPlainTextEdit widget should show standard output of one of 2 worker threads of my program respectively. The 2 worker threads were assigned different arguments to specify to which QPlainTextEdit widget their standard output should be redirected.
However, I found if I implemented this program by putting the "start" function of the instance of QThreadPool in a for-loop, both worker threads would be assigned arguments of the same value and their output would be redirected to the same QPlainTextEdit widget.
Below is the code of my main program:
from PyQt5 import QtCore, QtWidgets, QtGui
import sys, os
from mainWindow import Ui_Form
class MyMainWindow(QtWidgets.QDialog, Ui_Form):
def __init__(self, parent = None):
super(MyMainWindow, self).__init__(parent)
self.setupUi(self)
self.pushButton.clicked.connect(self.start_working)
sys.stdout = Stream(newText = self.update_text)
def start_working(self):
self.pool = QtCore.QThreadPool()
for taskID in {'taskA', 'taskB'}:
worker = Worker()
self.pool.start(lambda: worker.run(taskID))
def update_text(self, output: tuple):
displayer = self.plainTextEditA if output[0] == 'taskA' else self.plainTextEditB
cursor = displayer.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(f'{output[0]} - {output[1]}\n')
displayer.setTextCursor(cursor)
displayer.ensureCursorVisible()
class Worker(QtCore.QRunnable):
def run(self, taskID):
for index in range(1000):
sys.stdout.write((taskID, index))
class Stream(QtCore.QObject):
newText = QtCore.pyqtSignal(tuple)
def write(self, output: tuple):
self.newText.emit(output)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MyMainWindow()
window.show()
sys.exit(app.exec_())
Below is the code for GUI in the module named "mainWindow":
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(597, 298)
self.gridLayout = QtWidgets.QGridLayout(Form)
self.gridLayout.setObjectName("gridLayout")
self.label = QtWidgets.QLabel(Form)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.label_2 = QtWidgets.QLabel(Form)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 0, 2, 1, 1)
self.plainTextEditA = QtWidgets.QPlainTextEdit(Form)
self.plainTextEditA.setObjectName("plainTextEditA")
self.gridLayout.addWidget(self.plainTextEditA, 1, 0, 1, 2)
self.plainTextEditB = QtWidgets.QPlainTextEdit(Form)
self.plainTextEditB.setObjectName("plainTextEditB")
self.gridLayout.addWidget(self.plainTextEditB, 1, 2, 1, 2)
spacerItem = QtWidgets.QSpacerItem(376, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout.addItem(spacerItem, 2, 1, 1, 2)
self.pushButton = QtWidgets.QPushButton(Form)
self.pushButton.setObjectName("pushButton")
self.gridLayout.addWidget(self.pushButton, 2, 3, 1, 1)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.label.setText(_translate("Form", "A"))
self.label_2.setText(_translate("Form", "B"))
self.pushButton.setText(_translate("Form", "PushButton"))
I also found if I changed the "start_working" function by moving the "start" function of the QThreadPool instance to an independent function like "run_thread" in the following code, the output of worker threads would be redirected to the correct QPlainTextEdit widgets.
def start_working(self):
self.pool = QtCore.QThreadPool()
for taskID in {'taskA', 'taskB'}:
self.run_thread(taskID)
def run_thread(self, taskID: str):
worker = Worker(self)
self.pool.start(lambda: worker.run(taskID)
Can anyone tell me why this is so?