I would like to run a progress bar in a different thread from the rest of my code, but I would like to control how the progress bar updates from my main thread.
Is this something which is possible?
This is what I have so far:
import time
from PySide import QtGui
from PySide import QtCore
from PySide import QtUiTools
class progressBar(QtGui.QDialog, QtCore.QThread):
def __init__(self, window, title=None):
super(progressBar, self).__init__(window)
QtCore.QThread.__init__(self)
self.title = title or 'Progress'
self.setupUi()
self.show()
def setupUi(self):
self.setObjectName("Thinking")
self.gridLayout = QtGui.QGridLayout(self)
self.gridLayout.setObjectName("gridLayout")
self.progressBar = QtGui.QProgressBar(self)
self.gridLayout.addWidget(self.progressBar, 0, 0, 1, 1)
# ADJUSTMENTS
self.setMaximumSize(280, 50)
self.setMinimumSize(280, 50)
self.setWindowTitle(self.title)
def increase(self, inc):
self.progressBar.setProperty("value", inc)
time.sleep(0.01)
def run(self):
for i in range(1,101):
self.increase(i)
progressThread = progressBar(QtGui.QApplication.activeWindow())
progressThread.start()
This seems to be running the progress bar correctly inside of the thread, but it is controlled completely by the run function.
I tried removing the run function and adding this code to my main thread:
progressThread = progressBar(QtGui.QApplication.activeWindow())
progressThread.start()
for i in range(1,101):
progressThread.increase(i)
But this didn't seem to work.
Any help with this would be great... Thanks
I believe bnaecker, Brendan Abel and Steve Cohen already gave you the key bits of info in their comments. As they said already, you can definitely run your progress bar and your logic in separate threads, provided the UI run on the main thread.
Here is an example which should work the way you want:
import time, random
import threading
from PySide import QtCore, QtGui
class ProgressWidget(QtGui.QWidget):
# just for the purpose of this example,
# define a fixed number of threads to run
nthreads = 6
def __init__(self):
super(ProgressWidget, self).__init__()
self.threads = []
self.workers = []
self.works = [0 for i in range(self.nthreads)]
self.setupUi()
self.setupWorkers()
self.runThreads()
def drawProgessBar(self):
self.progressBar = QtGui.QProgressBar(self)
self.progressBar.setGeometry(QtCore.QRect(20, 20, 582, 24))
self.progressBar.minimum = 1
self.progressBar.maximum = 100
self.progressBar.setValue(0)
def setupUi(self):
self.setWindowTitle("Threaded Progress")
self.resize(600, 60)
self.drawProgessBar()
def buildWorker(self, index):
"""a generic function to build multiple workers;
workers will run on separate threads and emit signals
to the ProgressWidget, which lives in the main thread
"""
thread = QtCore.QThread()
worker = Worker(index)
worker.updateProgress.connect(self.handleProgress)
worker.moveToThread(thread)
thread.started.connect(worker.work)
worker.finished.connect(thread.quit)
QtCore.QMetaObject.connectSlotsByName(self)
# retain a reference in the main thread
self.threads.append(thread)
self.workers.append(worker)
def setupWorkers(self):
for i in range(self.nthreads):
self.buildWorker(i)
def runThreads(self):
for thread in self.threads:
thread.start()
def handleProgress(self, signal):
"""you can add any logic you want here,
it will be executed in the main thread
"""
index, progress = signal
self.works[index] = progress
value = 0
for work in self.works:
value += work
value /= float(self.nthreads)
# management of special cases
if value >= 100:
self.progressBar.hide()
return
# else
self.progressBar.setValue(value)
print 'progress (ui) thread: %s (value: %d)' % (threading.current_thread().name, value)
class Worker(QtCore.QObject):
"""the worker for a threaded process;
(this is created in the main thread and
then moved to a QThread, before starting it)
"""
updateProgress = QtCore.Signal(tuple)
finished = QtCore.Signal(int)
def __init__(self, index):
super(Worker, self).__init__()
# store the Worker index (for thread tracking
# and to compute the overall progress)
self.id = index
def work(self):
for i in range(100):
print 'worker thread: %s' % (threading.current_thread().name, )
# simulate some processing time
time.sleep(random.random() * .2)
# emit progress signal
self.updateProgress.emit((self.id, i + 1))
# emit finish signal
self.finished.emit(1)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
ui = ProgressWidget()
ui.show()
sys.exit(app.exec_())
Here's a minimal breakdown:
moveToThread
method (workers need to subclass QObject
for that method to be available)A side note: in the console, the workers' output refers to "dummy" threads. This seems to be related to the fact that the threading
module has no knowledge of QThreads (that's what I got from here, at least).
Still, it seems enough to prove that workers' tasks are running on separate threads. If anyone's got more accurate info, feel free to expand.
For those want to read more on this topic, here is a link which many articles refer to.