For any task or dialog where you want to update a progress bar, I recommend using QgsTask
and QgsTaskManager
. This will allow the task to run in a background thread, and prevent your dialog window from freezing while the task is running. It also allows for cancelling of the task by the user at any time.
There are a few ways of using QgsTask
and QgsTaskManager
which are detailed here in the documentation.
Here is an example I have put together which updates a progress bar in a dialog window and shows how to stop the task with a cancel button from the dialog.
You can run this from the editor in the QGIS Python console.
from PyQt5.QtWidgets import *
import time
class TestTask(QgsTask):
"""Here we subclass QgsTask"""
def __init__(self, desc):
QgsTask.__init__(self, desc)
def run(self):
"""This function is where you do the 'heavy lifting' or implement
the task which you want to run in a background thread. This function
must return True or False and should only interact with the main thread
via signals"""
for i in range (21):
time.sleep(0.5)
val = i * 5
#report progress which can be received by the main thread
self.setProgress(val)
#check to see if the task has been cancelled
if self.isCanceled():
return False
return True
def finished(self, result):
"""This function is called automatically when the task is completed and is
called from the main thread so it is safe to interact with the GUI etc here"""
if result is False:
iface.messageBar().pushMessage('Task was cancelled')
else:
iface.messageBar().pushMessage('Task Complete')
class My_Dialog(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self, parent)
self.resize(500, 350)
self.lbl_info = QLabel('Info:', self)
self.lbl_info.move(100, 50)
self.edit_info = QLineEdit(self)
self.edit_info.move(200, 50)
lbl_prog = QLabel('Task Progress: ', self)
lbl_prog.move(100, 210)
self.prog = QProgressBar(self)
self.prog.resize(200, 30)
self.prog.move(200, 200)
btn_OK = QPushButton('OK', self)
btn_OK.move(300, 300)
btn_OK.clicked.connect(self.newTask)
btn_close = QPushButton('Close',self)
btn_close.move(400, 300)
btn_close.clicked.connect(self.close_win)
btn_cancel = QPushButton('Cancel Task', self)
btn_cancel.move(50, 300)
btn_cancel.clicked.connect(self.cancelTask)
def newTask(self):
"""Create a task and add it to the Task Manager"""
self.task = TestTask('Custom Task')
#connect to signals from the background threads to perform gui operations
#such as updating the progress bar
self.task.begun.connect(lambda: self.edit_info.setText('Working...'))
self.task.progressChanged.connect(lambda: self.prog.setValue(self.task.progress()))
self.task.taskCompleted.connect(lambda: self.edit_info.setText('Task Complete'))
self.task.taskTerminated.connect(self.TaskCancelled)
QgsApplication.taskManager().addTask(self.task)
def cancelTask(self):
self.task.cancel()
def TaskCancelled(self):
self.prog.setValue(0)
self.edit_info.setText('Task Cancelled')
def close_win(self):
My_Dialog.close(self)
m = My_Dialog()
m.show()
Edit:
Since your original question was about QProgressDialog, below is a minimal code snippet to show an updating QProgressDialog with a cancel button. This code works for me in the Python console editor in QGIS 3.4.1
from PyQt5.QtWidgets import *
import time
prog = QProgressDialog('Working...', 'Cancel', 0, 100)
prog.setWindowModality(Qt.WindowModal)
for i in range (1, 101):
time.sleep(0.05)
prog.setValue(i)
if prog.wasCanceled():
break