I'm trying to understand how to use threading with Pyqt and am really struggling following any tutorials as most of them are based on an interface created from scratch through classes. I have created my interface using Qt designer and cant figure out how to run the program using threading and update the interface without having it to freeze. My example code is shown below:
import sys
from PyQt5 import uic, QtWidgets
def task():
for i in range (1,100000):
html = ("<p style = 'color:blue;'> <b> Completed: %s <b> <p><br>") % i
window.plainTextEdit.appendHtml(html)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = uic.loadUi('test.ui')
window.show()
window.pushButton.clicked.connect(task)
sys.exit(app.exec_())
Would love to also add a progress bar that can run on its own thread too.
Classes you need to love, the sooner the better! One option for what you want may look like this:
import sys
import threading
from PyQt5 import QtWidgets, QtCore # ,uic
def thread(my_func):
""" Runs a function in a separate thread. """
def wrapper(*args, **kwargs):
my_thread = threading.Thread(target=my_func, args=args, kwargs=kwargs)
my_thread.start()
return wrapper
@thread
def processing(signal):
""" Emulates the processing of some data. """
ind = 1
for i in range (1,10001): # +++
html = ("<p style='color:blue;'> Completed: <b>%s </b> </p> <br>") % i
QtCore.QThread.msleep(5)
ind = ind if i%100!=0 else ind+1 # +++
signal.emit(html, ind) # +++
def mySignalHandler(html, val): # Called to process a signal
plainTextEdit.appendHtml(html)
progressBar.setValue(val)
class WindowSignal(QtWidgets.QWidget):
""" New signals can be defined as class attributes using the pyqtSignal() factory. """
my_signal = QtCore.pyqtSignal(str, int, name='my_signal')
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = WindowSignal()
button = QtWidgets.QPushButton("Emit your signal!", window)
button.clicked.connect(lambda: processing(window.my_signal))
# since you do not publish test.ui, I replaced it with the line below:
plainTextEdit = QtWidgets.QPlainTextEdit(window)
progressBar = QtWidgets.QProgressBar()
progressBar.setTextVisible(False)
layoutHBox = QtWidgets.QHBoxLayout()
layoutHBox.addWidget(button)
layoutHBox.addWidget(plainTextEdit)
layoutVBox = QtWidgets.QVBoxLayout()
window.setLayout(layoutVBox)
layoutVBox.addLayout(layoutHBox)
layoutVBox.addWidget(progressBar)
window.my_signal.connect(mySignalHandler, QtCore.Qt.QueuedConnection)
window.show()
sys.exit(app.exec_())
Example 2:
test.ui
is a file containing the description of the main window automatically generated by Qt Designer.
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QWidget" name="">
<property name="geometry">
<rect>
<x>12</x>
<y>12</y>
<width>371</width>
<height>281</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="button">
<property name="text">
<string>Emit your signal!</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="plainTextEdit"/>
</item>
</layout>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
main.py
import sys
import threading
from PyQt5 import QtWidgets, QtCore, uic
def thread(my_func):
""" Runs a function in a separate thread. """
def wrapper(*args, **kwargs):
my_thread = threading.Thread(target=my_func, args=args, kwargs=kwargs)
my_thread.start()
return wrapper
@thread
def processing(signal):
""" Emulates the processing of some data. """
for i in range (1,101): # (1,100000)
html = ("<p style='color:blue;'> Completed: <b>%s </b> </p> <br>") % i
QtCore.QThread.msleep(10)
signal.emit(html, i) # Send a signal in which we transfer the received data
def mySignalHandler(html, val): # Called to process a signal
window.plainTextEdit.appendHtml(html)
window.progressBar.setValue(val)
class WindowSignal(QtWidgets.QWidget):
""" New signals can be defined as class attributes using the pyqtSignal() factory. """
my_signal = QtCore.pyqtSignal(str, int, name='my_signal')
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = WindowSignal()
uic.loadUi('test.ui', window)
window.button.clicked.connect(lambda: processing(window.my_signal))
window.my_signal.connect(mySignalHandler, QtCore.Qt.QueuedConnection)
window.show()
sys.exit(app.exec_())