Python pyqt pulsing progress bar with multithreading - python

Please bear with my question as I am a beginner. I have been having problems implementing the progress bar in pyqt and all of the example I have seen doesn't really explain on how to implement it properly and from this example and this example I somewhat partially made it work but it still hangs. I have this code:
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setGeometry(750, 450, 400, 200)
self.setFixedSize(self.size())
btn1 = QtGui.QPushButton("Convert", self)
btn1.move(210,171)
btn1.clicked.connect(self.progbar)
def progbar (self):
self.prog_win = QDialog()
self.prog_win.resize(400, 100)
self.prog_win.setFixedSize(self.prog_win.size())
self.prog_win.setWindowTitle("Processing request")
self.lbl = QLabel(self.prog_win)
self.lbl.setText("Please Wait. . .")
self.lbl.move(15,18)
self.progressBar = QtGui.QProgressBar(self.prog_win)
self.progressBar.resize(410, 25)
self.progressBar.move(15, 40)
self.progressBar.setRange(0,1)
self.myLongTask = TaskThread()
#I think this is where I am wrong
#because all of the answers here is very specific
#or just not for beginners
self.prog_win.show()
self.myLongTask.taskFinished.connect(self.onStart)
self.output_settings()
def onStart(self):
self.progressBar.setRange(0,0)
self.myLongTask.start()
def output_convert(self):
#very long process to convert a txt file to excel
#My Thread
class TaskThread(QtCore.QThread):
taskFinished = QtCore.pyqtSignal()
def run(self):
time.sleep(3)
self.taskFinished.emit()
def run():
app = QtGui.QApplication(sys.argv)
GUI = Window()
app.exec_()
run()
All of the examples and posts here have been very helpful on understanding progress bar implementation but with all the example having specific answers for a specific problem I can't understand the implementation of progress bar in a standard pyqt app. could you guys at least point me in the right direction? Would be appreciated.

This is a very basic progress bar that only uses what is needed at the bare minimum.
It would be wise to read this whole example to the end.
import sys
import time
from PyQt5.QtWidgets import (QApplication, QDialog,
QProgressBar, QPushButton)
TIME_LIMIT = 100
class Actions(QDialog):
"""
Simple dialog that consists of a Progress Bar and a Button.
Clicking on the button results in the start of a timer and
updates the progress bar.
"""
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('Progress Bar')
self.progress = QProgressBar(self)
self.progress.setGeometry(0, 0, 300, 25)
self.progress.setMaximum(100)
self.button = QPushButton('Start', self)
self.button.move(0, 30)
self.show()
self.button.clicked.connect(self.onButtonClick)
def onButtonClick(self):
count = 0
while count < TIME_LIMIT:
count += 1
time.sleep(1)
self.progress.setValue(count)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Actions()
sys.exit(app.exec_())
The progress bar is first imported like so from PyQt5.QtWidgets import QProgressBar
Then it is initialized like any other widget in QtWidgets
The line self.progress.setGeometry(0, 0, 300, 25) method defines the x,y positions on the dialog and width and height of the progress bar.
We then move the button using .move() by 30px downwards so that there will be a gap of 5px between the two widgets.
Here self.progress.setValue(count) is used to update the progress. Setting a maximum value using .setMaximum() will also automatically calculated the values for you. For example, if the maximum value is set as 50 then since TIME_LIMIT is 100 it will hop from 0 to 2 to 4 percent instead of 0 to 1 to 2 every second. You can also set a minimum value using .setMinimum() forcing the progress bar to start from a given value.
Executing this program will produce a GUI similar to this.
As you can see, the GUI will most definitely freeze and be unresponsive until the counter meets the TIME_LIMIT condition. This is because time.sleep causes the OS to believe that program has become stuck in an infinite loop.
QThread
So how do we overcome this issue ? We can use the threading class that PyQt5 provides.
import sys
import time
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import (QApplication, QDialog,
QProgressBar, QPushButton)
TIME_LIMIT = 100
class External(QThread):
"""
Runs a counter thread.
"""
countChanged = pyqtSignal(int)
def run(self):
count = 0
while count < TIME_LIMIT:
count +=1
time.sleep(1)
self.countChanged.emit(count)
class Actions(QDialog):
"""
Simple dialog that consists of a Progress Bar and a Button.
Clicking on the button results in the start of a timer and
updates the progress bar.
"""
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('Progress Bar')
self.progress = QProgressBar(self)
self.progress.setGeometry(0, 0, 300, 25)
self.progress.setMaximum(100)
self.button = QPushButton('Start', self)
self.button.move(0, 30)
self.show()
self.button.clicked.connect(self.onButtonClick)
def onButtonClick(self):
self.calc = External()
self.calc.countChanged.connect(self.onCountChanged)
self.calc.start()
def onCountChanged(self, value):
self.progress.setValue(value)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Actions()
sys.exit(app.exec_())
Let's break down these modifications.
from PyQt5.QtCore import QThread, pyqtSignal
This line imports Qthread which is a PyQt5 implementation to divide and run some parts(eg: functions, classes) of a program in the background(also know as multi-threading). These parts are also called threads. All PyQt5 programs by default have a main thread and the others(worker threads) are used to offload extra time consuming and process intensive tasks into the background while still keeping the main program functioning.
The second import pyqtSignal is used to send data(signals) between worker and main threads. In this instance we will be using it to tell the main thread to update the progress bar.
Now we have moved the while loop for the counter into a separate class called External.
class External(QThread):
"""
Runs a counter thread.
"""
countChanged = pyqtSignal(int)
def run(self):
count = 0
while count < TIME_LIMIT:
count +=1
time.sleep(1)
self.countChanged.emit(count)
By sub-classing QThread we are essentially converting External into a class that can be run in a separate thread. Threads can also be started or stopped at any time adding to it's benefits.
Here countChanged is the current progress and pyqtSignal(int) tells the worker thread that signal being sent is of type int. While, self.countChanged.emit(count) simply sends the signal to any connections in the main thread(normally it can used to communicate with other worker threads as well).
def onButtonClick(self):
self.calc = External()
self.calc.countChanged.connect(self.onCountChanged)
self.calc.start()
def onCountChanged(self, value):
self.progress.setValue(value)
When the button is clicked the self.onButtonClick will run and also start the thread. The thread is started with .start(). It should also be noted that we connected the signal self.calc.countChanged we created earlier to the method used to update the progress bar value. Every time External::run::count is updated the int value is also sent to onCountChanged.
This is how the GUI could look after making these changes.
It should also feel much more responsive and will not freeze.

The answer to my own question. It is not that hard if you can understand the concept of threading and passing variables through classes. My first mistake was really the lack of knowledge about Worker threads, second is I thought that once you declare the Thread it means that you just have to call it so that it would run the function inside the main class so I was searching on how you would implement that and all I thought was wrong.
Solution
All the hard/Long processes SHOULD be in the subclassed QThread under def run and should be called in your class Window(QtGui.QMainWindow): or main loop and this is what my code look like now
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setGeometry(750, 450, 400, 200)
self.setFixedSize(self.size())
btn1 = QtGui.QPushButton("Convert", self)
btn1.move(210,171)
btn1.clicked.connect(self.progbar)
def progbar (self):
self.prog_win = QDialog()
self.prog_win.resize(400, 100)
self.prog_win.setFixedSize(self.prog_win.size())
self.prog_win.setWindowTitle("Processing request")
self.lbl = QLabel(self.prog_win)
self.lbl.setText("Please Wait. . .")
self.lbl.move(15,18)
self.progressBar = QtGui.QProgressBar(self.prog_win)
self.progressBar.resize(410, 25)
self.progressBar.move(15, 40)
self.progressBar.setRange(0,1)
self.myLongTask = TaskThread(var = DataYouWantToPass) #initializing and passing data to QThread
self.prog_win.show()
self.onStart() #Start your very very long computation/process
self.myLongTask.taskFinished.connect(self.onFinished) #this won't be read until QThread send a signal i think
def onStart(self):
self.progressBar.setRange(0,0)
self.myLongTask.start()
#added this function to close the progress bar
def onFinished(self):
self.progressBar.setRange(0,1)
self.prog_win.close()
#My Thread
class TaskThread(QtCore.QThread):
taskFinished = QtCore.pyqtSignal()
#I also added this so that I can pass data between classes
def __init__(self, var, parent=None):
QThread.__init__(self, parent)
self.var = var
def run(self):
#very long process to convert a txt file to excel
def run():
app = QtGui.QApplication(sys.argv)
GUI = Window()
app.exec_()
run()
If something in this answer is wrong then please correct me as it would be a great help to understand it more or maybe some dos and don't

Related

Periodic task in Qt without interrupting events

I have a graphic application in python using QWidget. The user can change elements by moving the mouse (using mouseMoveEvent), and the program should periodically (e.g., once per second) compute a function update_forces based on these elements.
Problem: the mouseMoveEvent doesn't trigger as often while update_forces is computed, so the program becomes periodically unresponsive. There are various tutorials online on how to remedy this using threads, but their solutions don't work for me, and I don't know why.
MWE
# Imports
import sys
import random
import time
from PyQt5.QtCore import QThread, pyqtSignal, QObject
from PyQt5.QtWidgets import QWidget, QApplication
# update_forces dummy
def update_forces():
for i in range(10000000):
x = i**2
# Worker object:
class Worker(QObject):
finished = pyqtSignal()
def run(self):
while(True):
update_forces()
time.sleep(1)
# and the Window class with a dummy mouseEvent that just prints a random number
class Window(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.setMouseTracking(True)
def initUI(self):
self.setGeometry(20, 20, 500, 500)
self.show()
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.thread.start()
def mouseMoveEvent(self, e):
print(random.random())
# head procedure
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
When I run this (Windows 10) and move my cursor around on the screen, I can clearly observe two phases: it goes from printing numbers rapidly (while update_forces is not computed), to printing them much more slowly (while it is computed). It keeps alternating between both.
The solution is to use multiprocessing rather than threading.
from multiprocessing import Process
if __name__ == '__main__':
p = Process(target=updater, args=())
p.start()
def updater(queue,test):
while(True):
update_forces()
time.sleep(1)
The problem with this is that no data is shared between processes by default, but that's solvable.

How to structure large pyqt5 GUI without subclassing QThread and using QPushButtons to do long-running tasks

I'm looking at creating a program with a PyQt5 GUI. The program will start with a UI with numerous buttons. These buttons will be used to open other programs/completed long running tasks. I know I need to use QThread, but I am unsure how to structure the programs so that it scales properly.
I've been at this for ages and have read numerous posts/tutorials. Most lean down the subclassing route. In the past, I have managed to create a working program subclassing QThread, but I have since read that this metholodogy is not preferred.
I have a feeling I should be creating a generic worker and passing in a function with *args and **kwargs, but that is not in my skillset yet.
I originally created a thread for each button during the GUI init, but that seemed like it was going to get out of hand quickly.
I am currently at the stage of creating a thread under the slot connected to the button.clicked signal. I am not sure if I then have to have a worker for each button or if I can/should make a generic worker and pass in a function. Note: I have tried to do this but have not been able to do it.
#Import standard modules
import sys
#Import third-party modles
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QVBoxLayout, QWidget
class Worker(QObject):
#Custom signals?? or built-in QThread signals?
started = pyqtSignal()
finished = pyqtSignal()
def __init__(self):
super().__init__()
self.started.emit()
#pyqtSlot()
def do_something(self):
for _ in range(3):
print('Threading...')
QThread.sleep(1)
self.finished.emit()
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.initUi()
def initUi(self):
#Create GUI
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget )
self.vertical_layout = QVBoxLayout(self.centralWidget)
self.setWindowTitle('QThread Test')
self.setGeometry(300, 300, 300, 50)
self.button1=QPushButton("Task 1", self, clicked=self._task1_clicked)
self.button2=QPushButton("Task 2", self, clicked=self._task2_clicked)
self.vertical_layout.addWidget(self.button1)
self.vertical_layout.addWidget(self.button2)
self.vertical_layout.addStretch()
def _task1_clicked(self):
print('task1 clicked')
#Create the worker
self.my_worker = Worker()
#Create thread; needs to be done before connecting signals/slots
self.task1_thread = QThread()
#Move the worker to the thread
self.my_worker.moveToThread(self.task1_thread)
#Connect worker and thread signals to slots
self.task1_thread.started.connect(self._thread_started)
self.task1_thread.started.connect(self.my_worker.do_something)
self.my_worker.finished.connect(self._thread_finished)
#Start thread
self.task1_thread.start()
def _task2_clicked(self):
print('task2 clicked')
def _thread_started(self):
print('thread started')
def _thread_finished(self):
print('thread finished')
self.my_worker.isRunning = False
self.task1_thread.quit()
self.task1_thread.wait()
print('The thread is running: ' + str(self.task1_thread.isRunning()))
if __name__ == '__main__':
app = QApplication(sys.argv)
form = Window()
form.show()
app.exec_()
The above seems to work, but I feel like I have stumbled on to it and it is not the correct way of doing this. I do not want this to be my 'go-to' method if it is completely wrong. I'd like to be able to generate more complicated (more buttons doing things) programs compared to a one button/one task program.
In addition, I can't seem to get the QThread started and finished signals to fire without basically making them custom built signals. This is one reason I think I am going about this wrong.
from PyQt5 import QtCore
class AsyncTask(QtCore.QThread):
taskDone = QtCore.pyqtSignal(dict)
def __init__(self, *, task, callback=None, parent = None):
super().__init__(parent)
self.task = task
if callback != None:
self.taskDone.connect(callback)
if callback == None:
callback = self.callback
self.start()
def run(self):
try:
result = self.task()
print(result)
self.taskDone.emit(result)
except Exception as ex:
print(ex)
def callback(self):
print('callback')
Please try code above, call like this:
AsyncTask(task=yourTaskFunction, callback=yourCallbackFunction)

QTimer on a Qthread

I have a GUI witch i need to update constantly using a Qtimer, for that I use a worker Qthread, this is my code :
from PyQt5.QtWidgets import QApplication, QPushButton, QWidget
from PyQt5.QtCore import QThread, QTimer
import sys
import threading
class WorkerThread(QThread):
def run(self):
print("thread started from :" + str(threading.get_ident()))
timer = QTimer(self)
timer.timeout.connect(self.work)
timer.start(5000)
self.exec_()
def work(self):
print("working from :" + str(threading.get_ident()))
QThread.sleep(5)
class MyGui(QWidget):
worker = WorkerThread()
def __init__(self):
super().__init__()
self.initUi()
print("Starting worker from :" + str(threading.get_ident()))
self.worker.start()
def initUi(self):
self.setGeometry(500, 500, 300, 300)
self.pb = QPushButton("Button", self)
self.pb.move(50, 50)
self.show()
app = QApplication(sys.argv)
gui = MyGui()
app.exec_()
the output is:
Starting worker from :824
thread started from :5916
working from :824
working from :824
the timer is working on the main thread witch freeze my Gui, How can i fix that ?
Answer: in your case I do not see the need to use QThread.
TL; DR;
When do I need to use another thread in the context of a GUI?
Only one thread should be used when some task can block the main thread called the GUI thread, and the blocking is caused because the task is time-consuming, preventing the GUI eventloop from doing its job normally. All modern GUIs are executed in an eventloop that is allows you to receive notifications from the OS like the keyboard, the mouse, etc. and also allows you to modify the status of the GUI depending on the user.
In your case I do not see any heavy task, so I do not see the need for a QThread, I do not really know what the task is that you want to run periodically.
Assuming you have a task that consumes a lot of time, say 30 seconds and you have to do it every half hour, then the thread is necessary. and in your case you want to use a QTimer for it.
Let's go in parts, QTimer is a class that inherits from a QObject, and a QObject belongs to the same as the parent, and if it does not have a parent it belongs to the thread where it was created. On the other hand many times it is thought that a QThread is a thread of Qt, but it is not, QThread is a class that allows to handle the life cycle of a native thread, and that is clearly stated in the docs: The QThread class provides a platform-independent way to manage threads.
Knowing the above, let's analyze your code:
timer = QTimer(self)
In the above code self is the parent of QTimer and self is the QThread, so QTimer belongs to the thread of the parent of QThread or where QThread was created, not to the thread that QThread handles.
Then let's see the code where QThread was created:
worker = WorkerThread()
As we see QThread has no parent, then QThread belongs to the thread where it was created, that is, QThread belongs to the main thread, and consequently its QTimer child also belongs to the main thread. Also note that the new thread that QThread handles only has the scope of the run() method , if the method is elsewhere belongs to the field where QThread was created, with all the above we see that the output of the code is correct, and the QThread.sleep(5) runs on the main thread causing the eventloop to crash and the GUI to freeze.
So the solution is to remove the parent of QTimer so that the thread it belongs to is the one of the run() method, and move the work function within the same method. On the other hand it is a bad practice to create static attributes unnecessarily, considering the above the resulting code is the following:
import sys
import threading
from PyQt5.QtCore import QThread, QTimer
from PyQt5.QtWidgets import QApplication, QPushButton, QWidget
class WorkerThread(QThread):
def run(self):
def work():
print("working from :" + str(threading.get_ident()))
QThread.sleep(5)
print("thread started from :" + str(threading.get_ident()))
timer = QTimer()
timer.timeout.connect(work)
timer.start(10000)
self.exec_()
class MyGui(QWidget):
def __init__(self):
super().__init__()
self.initUi()
self.worker = WorkerThread(self)
print("Starting worker from :" + str(threading.get_ident()))
self.worker.start()
def initUi(self):
self.setGeometry(500, 500, 300, 300)
self.pb = QPushButton("Button", self)
self.pb.move(50, 50)
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = MyGui()
gui.show()
sys.exit(app.exec_())
Output:
Starting worker from :140068367037952
thread started from :140067808999168
working from :140067808999168
working from :140067808999168
Observations:
The heavy task that has been emulated is 5 seconds, and that task must be executed every 10 seconds. If your task takes longer than the period you should create other threads.
If your task is to perform a periodic task that is not as heavy as showing time then do not use new threads because you are adding complexity to a simple task, besides this may cause the debugging and testing stage to be more complex.
Sorry, I have misunderstood the question. It might be that this answer on another question might help you. The main message is that you should use the main event loop in Qt to not freeze the GUI instead of executing the thread on __init__:
Pyqt5 qthread + signal not working + gui freeze
You can do that by using Qt slots with the decorator #pyqtSlot().
------------ old (wrong) answer ---------
QTimer can already work on separate threads, so I think you might be able to do it without writing that part yourself. For example you could do what you already do in a function:
def update_gui(self):
# edit: here is where you can add your gui update code:
self.setGeometry(500, 500, 300, 300)
self.pb = QPushButton("Button", self)
self.pb.move(50, 50)
self.show()
# /edit (this is obviously only the setup code, but you get the idea)
self.update_timer = QTimer()
self.update_timer.setInterval(int(5000))
self.update_timer.timeout.connect(self.update_gui)
self.update_timer.start()
and call it in __init__. That's how I implemented some textbox that clears itself after a couple of seconds.
Try it:
import sys
import threading
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class WorkerThread(QThread):
workSignal = pyqtSignal(str)
def run(self):
print("thread started from :" + str(threading.get_ident()))
textLabel = "thread started from :" + str(threading.get_ident())
self.workSignal.emit(textLabel)
self.work()
def work(self):
print("working from :" + str(threading.get_ident()))
textLabel = "working from :" + str(threading.get_ident())
self.workSignal.emit(textLabel)
class MyGui(QWidget):
worker = WorkerThread()
def __init__(self):
super().__init__()
self.initUi()
print("Starting worker from :" + str(threading.get_ident()))
self.lbl.setText("Starting worker from :" + str(threading.get_ident()))
self.worker.workSignal.connect(self.showLabel)
def initUi(self):
self.setGeometry(700, 350, 300, 150)
self.lcdTime = QLCDNumber(self)
self.lcdTime.setSegmentStyle(QLCDNumber.Filled) # Outline Filled Flat
self.lcdTime.setDigitCount(8)
self.timer = QTimer(self)
self.lbl = QLabel(self)
self.pb = QPushButton("Button Close", self, clicked=self.close)
vbox = QVBoxLayout()
vbox.addWidget(self.lcdTime)
vbox.addWidget(self.lbl)
vbox.addWidget(self.pb)
self.setLayout(vbox)
self.timer.timeout.connect(self.showTime)
self.timer.start(1000)
self.numSec = 0
self.show()
def showTime(self):
time = QTime.currentTime()
text = time.toString("hh:mm:ss")
if ((time.second() % 2) == 0):
text = text[0:2] + ' ' + text[3:5] + ' ' + text[6:]
self.lcdTime.display(text)
self.numSec += 1
if self.numSec == 5:
self.worker.start()
self.numSec = 0
def showLabel(self, textLabel):
self.lbl.setText(textLabel)
app = QApplication(sys.argv)
gui = MyGui()
app.exec_()

How to display progress without multi-threading

Let's say I have a PyQt program that goes through a given directory, looks for *JPEG images, and does some processing every time it finds one. Depending on the size of the selected directory, this may take from some seconds to minutes.
I would like to keep my user updated with the status - preferably with something like "x files processed out of y files" . If not, a simple running pulse progress bar by setting progressbar.setRange(0,0) works too.
From my understanding, in order to prevent my GUI from freezing, I will need a seperate thread that process the images, and the original thread that updates the GUI every interval.
But I am wondering if there is any possible way for me to do both in the same thread?
Yes, you can easily do this using processEvents, which is provided for this exact purpose.
I have used this technique for implementing a simple find-in-files dialog box. All you need to do is launch the function that processes the files with a single-shot timer, and then periodically call processEvents in the loop. This is should be good enough to update a counter with the number of files processed, and also allow the user to cancel the process, if necessary.
The only real issue is deciding on how frequently to call processEvents. The more often you call it, the more responsive the GUI will be - but this comes at the cost of considerably slowing the processing of the files. So you may have to experiment a little bit in order to find an acceptable compromise.
UPDATE:
Here's a simple demo that shows how the code could be structured:
import sys, time
from PyQt5 import QtWidgets, QtCore
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.button = QtWidgets.QPushButton('Start')
self.progress = QtWidgets.QLabel('0')
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.progress)
self.button.clicked.connect(self.test)
self._stop = False
self._stopped = True
def test(self):
if self._stopped:
self._stop = False
self.progress.setText('0')
self.button.setText('Stop')
QtCore.QTimer.singleShot(1, self.process)
else:
self._stop = True
def process(self):
self._stopped = False
for index in range(1, 1000):
time.sleep(0.01)
self.progress.setText(str(index))
if not index % 20:
QtWidgets.qApp.processEvents(
QtCore.QEventLoop.AllEvents, 50)
if self._stop:
break
self._stopped = True
self.button.setText('Start')
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I could not achieve the thing you need without multi threading and this is not possible because gui can be only updated in main thread. Below is an algorithm how I did this with multithreading.
Let's say you have your application processing images. Then there are the following threads:
Main thread (that blocks by GUI/QApplication-derived classes.exec())
Timer with, for example, 1 second interval which updates a variable and calls a slot in GUI thread which updates a variable in user interface.
A thread which is processing images on your pc.
def process(self):
self._status = "processing image 1"
....
def _update(self):
self.status_label.setText(self._status)
def start_processing(self, image_path):
# create thread for process and run it
# create thread for updating by using QtCore.QTimer()
# connect qtimer triggered signal to and `self._update()` slot
# connect image processing thread (use connect signal to any slot, in this example I'll stop timer after processing thread finishes)
#pyqtSlot()
def _stop_timer():
self._qtimer.stop()
self._qtimer = None
_update_thread.finished.connect(_stop_timer)
In pyqt5 it is possible to assign a pyqtvariable from a one nested thread(first level). So you can make your variable a pyqtvariable with setter and getter and update gui in a setter or think how you can do this by yourself.
You could just use the python threading module and emit a signal in your threaded routine.
Here's a working example
from PyQt4 import QtGui, QtCore
import threading
import time
class MyWidget(QtGui.QWidget):
valueChanged = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.computeButton = QtGui.QPushButton("Compute", self)
self.progressBar = QtGui.QProgressBar()
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.computeButton)
layout.addWidget(self.progressBar)
self.computeButton.clicked.connect(self.compute)
self.valueChanged.connect(self.progressBar.setValue)
def compute(self):
nbFiles = 10
self.progressBar.setRange(0, nbFiles)
def inner():
for i in range(1, nbFiles+1):
time.sleep(0.5) # Process Image
self.valueChanged.emit(i) # Notify progress
self.thread = threading.Thread(target = inner)
self.thread.start()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())

PyQt Progressbar QThread does not work correct

I have a problem with my code. My plan is to show the progress of a for loop using a progressbar. My idea was to use Qthread. My code below works somehow, but not 100 percently correct. The progressbar shows the progress of the for loop, but not via a thread i.e. if I try to click more then once on Stop the GUI freezes.
I am not a QtCore expert. Can please someone help me and tell me why it does not work the way I want it to work?
Thanks a lot!
from PyQt4 import QtGui, QtCore
#Progressbar
class MyCustomWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MyCustomWidget, self).__init__(parent)
layout = QtGui.QVBoxLayout(self)
self.progressBar = QtGui.QProgressBar(self)
self.progressBar.setRange(0,100)
layout.addWidget(self.progressBar)
#Update Progressbar
def onProgress(self, i):
self.progressBar.setValue(i)
if self.progressBar.value() >= self.progressBar.maximum():
self.close()
#Threading Class
class ASA(QtCore.QThread):
notifyProgress = QtCore.pyqtSignal(int)
def run(self, i):
#Sends the new information to the Update Function
self.notifyProgress.emit(i)
time.sleep(0.01)
#-----------------------------------------#
#Main Function
app = QtGui.QApplication(sys.argv)
bar = MyCustomWidget()
bar.show()
bar.asa = ASA()
bar.asa.notifyProgress.connect(bar.onProgress)
bar.asa.start()
#For loop for the progressbar
for i in range(105):
ASA.run(bar.asa, i)
time.sleep(0.5)
sys.exit(app.exec_())
The loop needs to be run inside the thread itself:
def run(self):
#Sends the new information to the Update Function
for i in range(105):
self.notifyProgress.emit(i)
time.sleep(0.01)

Categories

Resources