I'm working on a GUI for calling a function 'my_function' when the button 'my_button' is pushed.
This function processes data iteratively. It contains a 'for' loop, and at each iteration it prints out a message that shows the progress of my function. I would like this prints to be displayed in my GUI (in this example in a textEdit widget), in real time. How could I do that?
I would like to make it clear that I need real time display. I found some solutions online, but all prints only appear when the function finishes execution. I need the prints to display in real time in order to appreciate the function progress. Thanks in advance for your tips!
Here's a minimal reproductible example (I obtained the template via qt designer):
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(163, 225)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.formLayout = QtWidgets.QFormLayout(self.centralwidget)
self.formLayout.setObjectName("formLayout")
self.my_button = QtWidgets.QPushButton(self.centralwidget)
self.my_button.setObjectName("my_button")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.my_button)
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
self.textEdit.setObjectName("textEdit")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.textEdit)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 163, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.my_button.clicked.connect(self.my_function)
def my_function(self):
for idx in range(10):
print('Executing iteration '+str(idx)+' ...')
time.sleep(1) # replaces time-consuming task
print('Finished!')
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.my_button.setText(_translate("MainWindow", "Execute"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Golden rule of Qt: You should not execute tasks that consume a lot of time in the main thread of the GUI since they block the event loop of Qt.
Considering the above, you must execute the time-consuming task in another thread and send the information to the GUI thread through signals.
On the other hand you should not modify the class generated by Qt Designer, instead create another class that inherits from the appropriate widget and is filled in with the initial class.
import threading
import time
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(163, 225)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.formLayout = QtWidgets.QFormLayout(self.centralwidget)
self.formLayout.setObjectName("formLayout")
self.my_button = QtWidgets.QPushButton(self.centralwidget)
self.my_button.setObjectName("my_button")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.my_button)
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
self.textEdit.setObjectName("textEdit")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.textEdit)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 163, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.my_button.setText(_translate("MainWindow", "Execute"))
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
valueChanged = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.valueChanged.connect(self.on_value_changed)
self.my_button.clicked.connect(self.on_clicked)
@QtCore.pyqtSlot()
def on_clicked(self):
threading.Thread(target=self.my_function, daemon=True).start()
@QtCore.pyqtSlot(int)
def on_value_changed(self, value):
self.textEdit.append("Value: {}".format(value))
def my_function(self):
for idx in range(10):
self.valueChanged.emit(idx)
print("Executing iteration " + str(idx) + " ...")
time.sleep(1) # replaces time-consuming task
print("Finished!")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With