PyQt Progress Bar Update with Threads
There are really two problems. One thing I have noticed is that Python threads are greedy if they are not used for IO operations like reading from a serial port. If you tell a thread to run a calculation or something that is not IO related a thread will take up all of the processing and doesn't like to let the main thread / event loop run. The second problem is that signals are slow ... very slow. I've noticed that if you emit a signal from a thread and do it very fast it can drastically slow down a program.
So at the heart of the issue, the thread is taking up all of the time and you are emitting a signal very very fast which will cause slow downs.
For clarity and ease of use I would use the new style signal and slots. http://pyqt.sourceforge.net/Docs/PyQt4/new_style_signals_slots.html
class progressThread(QThread):
progress_update = QtCore.Signal(int) # or pyqtSignal(int)
def __init__(self):
QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
# your logic here
while 1:
maxVal = 100
self.progress_update.emit(maxVal) # self.emit(SIGNAL('PROGRESS'), maxVal)
# Tell the thread to sleep for 1 second and let other things run
time.sleep(1)
To connect to the new style signal
...
self.progress_thread.start()
self.process_thread.progress_update.connect(self.updateProgressBar) # self.connect(self.progress_thread, SIGNAL('PROGRESS'), self.updateProgressBar)
...
EDIT Sorry there is another problem. When a signal calls a function you cannot stay in that function forever. That function is not running in a separate thread it is running on the main event loop and the main event loop waits to run until you exit that function.
Update progress sleeps for 1 second and keeps looping. The hanging is coming from staying in this function.
def updateProgressBar(self, maxVal):
for i in range(maxVal):
self.ui.progressBar.setValue(self.ui.progressBar.value() + 1)
time.sleep(1)
maxVal = maxVal - 1
if maxVal == 0:
self.ui.progressBar.setValue(100)
It would be better to write the progress bar like
class progressThread(QThread):
progress_update = QtCore.Signal(int) # or pyqtSignal(int)
def __init__(self):
QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
# your logic here
while 1:
maxVal = 1 # NOTE THIS CHANGED to 1 since updateProgressBar was updating the value by 1 every time
self.progress_update.emit(maxVal) # self.emit(SIGNAL('PROGRESS'), maxVal)
# Tell the thread to sleep for 1 second and let other things run
time.sleep(1)
def updateProgressBar(self, maxVal):
self.ui.progressBar.setValue(self.ui.progressBar.value() + maxVal)
if maxVal == 0:
self.ui.progressBar.setValue(100)
nuriselcuk
I love to help people who has struggled about codes. Love the support and share my thoughts with any other people in world
Updated on June 04, 2022Comments
-
nuriselcuk almost 2 years
I have been writing a program which runs a remote script on server. So, I need to show the progress with a bar but somehow when I run my code, GUI start to freeze. I have used QThread and SIGNAL but unfortunately couldnt be succedeed.
Here is my code below;
class dumpThread(QThread): def __init__(self): QThread.__init__(self) def __del__(self): self.wait() def sendEstablismentCommands(self, connection): # Commands are sending sequently with proper delay-timers # connection.sendShell("telnet localhost 21000") time.sleep(0.5) connection.sendShell("admin") time.sleep(0.5) connection.sendShell("admin") time.sleep(0.5) connection.sendShell("cd imdb") time.sleep(0.5) connection.sendShell("dump subscriber") command = input('$ ') def run(self): # your logic here # self.emit(QtCore.SIGNAL('THREAD_VALUE'), maxVal) self.sendEstablismentCommands(connection) class progressThread(QThread): def __init__(self): QThread.__init__(self) def __del__(self): self.wait() def run(self): # your logic here while 1: maxVal = 100 self.emit(SIGNAL('PROGRESS'), maxVal) class Main(QtGui.QMainWindow): def __init__(self): QtGui.QMainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.connectButton.clicked.connect(self.connectToSESM) def connectToSESM(self): ## Function called when pressing connect button, input are being taken from edit boxes. ## ## dumpThread() method has been designed for working thread seperate from GUI. ## # Connection data are taken from "Edit Boxes" # username has been set as hardcoded ### Values Should Be Defined As Global ### username = "ntappadm" password = self.ui.passwordEdit.text() ipAddress = self.ui.ipEdit.text() # Connection has been established through paramiko shell library global connection connection = pr.ssh(ipAddress, username, password) connection.openShell() pyqtRemoveInputHook() # For remove unnecessary items from console global get_thread get_thread = dumpThread() # Run thread - Dump Subscriber self.progress_thread = progressThread() self.progress_thread.start() self.connect(self.progress_thread, SIGNAL('PROGRESS'), self.updateProgressBar) get_thread.start() def updateProgressBar(self, maxVal): for i in range(maxVal): self.ui.progressBar.setValue(self.ui.progressBar.value() + 1) time.sleep(1) maxVal = maxVal - 1 if maxVal == 0: self.ui.progressBar.setValue(100) def parseSubscriberList(self): parsing = reParser() def done(self): QtGui.QMessageBox.information(self, "Done!", "Done fetching posts!") if __name__ == "__main__": app = QtGui.QApplication(sys.argv) main = Main() main.show() sys.exit(app.exec_())
I am expecting to see updateProgressBar method has called with SIGNAL, so process goes through seperate thread. I coudlnt find where I am missing.
Thanks for any help
-
nuriselcuk about 7 yearsThanks for your answer, but my Gui is still freezing. Looks like no change. By the way I used pyqtSignal(int)
-
justengel about 7 yearsI edited my answer and added information about staying in updateProgressBar for too long.
-
nuriselcuk about 7 yearsYes, you are right, code is waiting for end up the for loop in progressBarUpdate. I have updated now it's working. Thanks for all