'Why should the slot function which receives signals for the standard output be in the main thread when making a GUI by PyQT5?
I have a GUI made by PyQT5. It has a painTextEdit widget to which I want to redirect the standard output of my worker thread. I create a class called "Stream" to emit signals which carry the standard out of the worker thread and a slot function called "update_text" will receive the signal from "Stream".
However, I found that if I create the slot function "update_text" inside the class for worker thread (called "Worker"), the painTextEdit widget only shows part of the standard output of the worker thread. On the other hands, after I move the function "update_text" to the class for the main stream, the standard output can be displayed completely.
Below is the sample of my main program in which the slot function "update_text" is implemented inside the class (called "Worker") for the worker thread. And it can only show part of the standard output on the GUI.
from PyQt5 import QtCore, QtGui, QtWidgets
from main_window import Ui_Form
import sys
class Stream(QtCore.QObject):
newText = QtCore.pyqtSignal(str)
def write(self, data: str):
self.newText.emit(data)
class Worker(QtCore.QRunnable):
def __init__(self, displayer: QtWidgets.QWidget):
super().__init__()
self.displayer = displayer
sys.stdout = Stream(newText = self.update_text)
def run(self):
for i in range(1, 101):
sys.stdout.write(f'{i}\n')
def update_text(self, consoleOutput: str):
cursor = self.displayer.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(consoleOutput)
self.displayer.setTextCursor(cursor)
self.displayer.ensureCursorVisible()
class MyMainWindow(QtWidgets.QWidget, Ui_Form):
def __init__(self, parent = None):
super(MyMainWindow, self).__init__(parent)
self.setupUi(self)
self.threadPool = QtCore.QThreadPool()
self.pushButtonStart.clicked.connect(self.start_working)
def start_working(self):
displayer = self.plainTextEdit
worker = Worker(displayer)
self.threadPool.start(worker.run)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MyMainWindow()
window.show()
sys.exit(app.exec_())
Below is another version of my program in which the slot function "update_text" is implemented inside the class (called "MyMainWindow") for the main thread. And it works fine.
from PyQt5 import QtCore, QtGui, QtWidgets
from main_window import Ui_Form
import sys
class Stream(QtCore.QObject):
newText = QtCore.pyqtSignal(tuple)
def write(self, data: tuple):
self.newText.emit(data)
class Worker(QtCore.QRunnable):
def __init__(self):
super().__init__()
def run(self, displayer: QtWidgets.QWidget):
for i in range(1, 1001):
data = (f'{i}\n', displayer)
sys.stdout.write(data)
class MyMainWindow(QtWidgets.QWidget, Ui_Form):
def __init__(self, parent = None):
super(MyMainWindow, self).__init__(parent)
self.setupUi(self)
self.threadPool = QtCore.QThreadPool()
sys.stdout = Stream(newText = self.update_text)
self.pushButtonStart.clicked.connect(self.start_working)
def start_working(self):
displayer = self.plainTextEdit
worker = Worker()
self.threadPool.start(lambda: worker.run(displayer))
def update_text(self, data: tuple):
consoleOutput, displayer = data
cursor = displayer.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(consoleOutput)
displayer.setTextCursor(cursor)
displayer.ensureCursorVisible()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MyMainWindow()
window.show()
sys.exit(app.exec_())
Below is my code for the module "main_window" which implements the GUI of PyQT5.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(600, 300)
self.gridLayout = QtWidgets.QGridLayout(Form)
self.gridLayout.setObjectName("gridLayout")
self.plainTextEdit = QtWidgets.QPlainTextEdit(Form)
self.plainTextEdit.setObjectName("plainTextEdit")
self.gridLayout.addWidget(self.plainTextEdit, 0, 0, 1, 1)
spacerItem = QtWidgets.QSpacerItem(500, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.pushButtonStart = QtWidgets.QPushButton(Form)
self.pushButtonStart.setObjectName("pushButtonStart")
self.gridLayout.addWidget(self.pushButtonStart, 1, 0, 1, 1)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.pushButtonStart.setText(_translate("Form", "Start"))
I wish to know why the GUI can not display all the standard output from the worker thread when the slot function "update_text" is inside the class for the worker thread. Must it be in the class for the main thread?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
