Updating QProgressBars running on thread with values from main
-
Hi, I am not well versed in programming and I am pretty new to Python and QT, so I am sorry if I will present my question wrong and I would be glad to try and elaborate on it in a different way.
What I would like to do is change the value of the progress bars on my GUI (that is running on a thread). This value would be calculated outside the thread, in the "main code".
Although I can change the value of the progressbars on initialization what I would really like to be able to do is change them dinamically.
val = 0 class Ui(QtWidgets.QMainWindow): def __init__(self): super(Ui, self).__init__() uic.loadUi('PBL3_designer.ui', self) self.progbar1= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar1') self.progbar2= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar2') self.progbar3= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar3') # Function to update prog bars def updateProgressBar(self): self.prog_bar_cinza1.setValue(val) self.prog_bar_cinza2.setValue(val*2) self.prog_bar_cores1.setValue(val*3) def interface(): app = QtWidgets.QApplication(sys.argv) ex = Ui() app.exec_() # MAIN # Starting the thread t = threading.Thread(target=interface) t.daemon = True t.start() time.sleep(1)
How how I be able to call
updateProgressBar()
from outside the thread?I've tried JonB's solution but I am not sure I am placing it right (at the moment it is defined outside the Ui class) I get the error
AttributeError: type object 'QMainWindow' has no attribute 'instance'
The full code I am running right now to try and change the prog bars is as follows:
import PyQt5.QtWidgets from PyQt5 import QtWidgets, uic import pbl3_rc import sys import time import threading import typing val = 0 class Ui(QtWidgets.QMainWindow): def __init__(self): super(Ui, self).__init__() uic.loadUi('PBL3_designer.ui', self) self.progbar1= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar1') self.progbar2= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar2') self.progbar3= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar3') # Function to update prog bars def updateProgressBar(self): self.progbar1.setValue(val) self.progbar2.setValue(val*2) self.progbar3.setValue(val*3) # JonB's solution def findMainWindow() -> typing.Union[QtWidgets.QMainWindow, None]: app = QtWidgets.QMainWindow.instance() for widget in app.topLevelWidgets(): if isinstance(widget, QtWidgets.QMainWindow): return widget return None def interface(): app = QtWidgets.QApplication(sys.argv) ex = Ui() app.exec_() # MAIN # Starting the thread t = threading.Thread(target=interface) t.daemon = True t.start() time.sleep(1) # Trying to find Main Window gui = findMainWindow() for i in range(5): val += 1 # Trying to update prog bar value gui.updateProgressBar() while True: ();
Thank you for reading :)
-
Hi, I am not well versed in programming and I am pretty new to Python and QT, so I am sorry if I will present my question wrong and I would be glad to try and elaborate on it in a different way.
What I would like to do is change the value of the progress bars on my GUI (that is running on a thread). This value would be calculated outside the thread, in the "main code".
Although I can change the value of the progressbars on initialization what I would really like to be able to do is change them dinamically.
val = 0 class Ui(QtWidgets.QMainWindow): def __init__(self): super(Ui, self).__init__() uic.loadUi('PBL3_designer.ui', self) self.progbar1= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar1') self.progbar2= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar2') self.progbar3= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar3') # Function to update prog bars def updateProgressBar(self): self.prog_bar_cinza1.setValue(val) self.prog_bar_cinza2.setValue(val*2) self.prog_bar_cores1.setValue(val*3) def interface(): app = QtWidgets.QApplication(sys.argv) ex = Ui() app.exec_() # MAIN # Starting the thread t = threading.Thread(target=interface) t.daemon = True t.start() time.sleep(1)
How how I be able to call
updateProgressBar()
from outside the thread?I've tried JonB's solution but I am not sure I am placing it right (at the moment it is defined outside the Ui class) I get the error
AttributeError: type object 'QMainWindow' has no attribute 'instance'
The full code I am running right now to try and change the prog bars is as follows:
import PyQt5.QtWidgets from PyQt5 import QtWidgets, uic import pbl3_rc import sys import time import threading import typing val = 0 class Ui(QtWidgets.QMainWindow): def __init__(self): super(Ui, self).__init__() uic.loadUi('PBL3_designer.ui', self) self.progbar1= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar1') self.progbar2= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar2') self.progbar3= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar3') # Function to update prog bars def updateProgressBar(self): self.progbar1.setValue(val) self.progbar2.setValue(val*2) self.progbar3.setValue(val*3) # JonB's solution def findMainWindow() -> typing.Union[QtWidgets.QMainWindow, None]: app = QtWidgets.QMainWindow.instance() for widget in app.topLevelWidgets(): if isinstance(widget, QtWidgets.QMainWindow): return widget return None def interface(): app = QtWidgets.QApplication(sys.argv) ex = Ui() app.exec_() # MAIN # Starting the thread t = threading.Thread(target=interface) t.daemon = True t.start() time.sleep(1) # Trying to find Main Window gui = findMainWindow() for i in range(5): val += 1 # Trying to update prog bar value gui.updateProgressBar() while True: ();
Thank you for reading :)
@TheAlmeida said in Updating QProgressBars running on thread with values from main:
How how I be able to call updateProgressBar() from outside the thread?
Using https://doc.qt.io/qt-5/signalsandslots.html
The thread calculating the values will emit a signal and pass current value as signal parameter. This signal will be connected to a slot in your GUI thread in your UI class. In this slot you will update the progress bar. -
Hi, I am not well versed in programming and I am pretty new to Python and QT, so I am sorry if I will present my question wrong and I would be glad to try and elaborate on it in a different way.
What I would like to do is change the value of the progress bars on my GUI (that is running on a thread). This value would be calculated outside the thread, in the "main code".
Although I can change the value of the progressbars on initialization what I would really like to be able to do is change them dinamically.
val = 0 class Ui(QtWidgets.QMainWindow): def __init__(self): super(Ui, self).__init__() uic.loadUi('PBL3_designer.ui', self) self.progbar1= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar1') self.progbar2= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar2') self.progbar3= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar3') # Function to update prog bars def updateProgressBar(self): self.prog_bar_cinza1.setValue(val) self.prog_bar_cinza2.setValue(val*2) self.prog_bar_cores1.setValue(val*3) def interface(): app = QtWidgets.QApplication(sys.argv) ex = Ui() app.exec_() # MAIN # Starting the thread t = threading.Thread(target=interface) t.daemon = True t.start() time.sleep(1)
How how I be able to call
updateProgressBar()
from outside the thread?I've tried JonB's solution but I am not sure I am placing it right (at the moment it is defined outside the Ui class) I get the error
AttributeError: type object 'QMainWindow' has no attribute 'instance'
The full code I am running right now to try and change the prog bars is as follows:
import PyQt5.QtWidgets from PyQt5 import QtWidgets, uic import pbl3_rc import sys import time import threading import typing val = 0 class Ui(QtWidgets.QMainWindow): def __init__(self): super(Ui, self).__init__() uic.loadUi('PBL3_designer.ui', self) self.progbar1= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar1') self.progbar2= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar2') self.progbar3= self.findChild(PyQt5.QtWidgets.QProgressBar, 'progbar3') # Function to update prog bars def updateProgressBar(self): self.progbar1.setValue(val) self.progbar2.setValue(val*2) self.progbar3.setValue(val*3) # JonB's solution def findMainWindow() -> typing.Union[QtWidgets.QMainWindow, None]: app = QtWidgets.QMainWindow.instance() for widget in app.topLevelWidgets(): if isinstance(widget, QtWidgets.QMainWindow): return widget return None def interface(): app = QtWidgets.QApplication(sys.argv) ex = Ui() app.exec_() # MAIN # Starting the thread t = threading.Thread(target=interface) t.daemon = True t.start() time.sleep(1) # Trying to find Main Window gui = findMainWindow() for i in range(5): val += 1 # Trying to update prog bar value gui.updateProgressBar() while True: ();
Thank you for reading :)
@TheAlmeida
I am @JonB, the author of thatfindMainWindow()
Python function. You have copied it correctly, but you are not using it in the right place/way.However, you have no need of it here.
- Make
updateProgressBar()
acceptval
as a parameter. - If your flavor of PyQt/PySide requires it, mark that method as a slot.
- In your thread define a signal, which takes
val
as a parameter. - And there
emit()
the signal, with a suitable value forval
, as progress is made through the "calculation". - Somewhere --- probably in your
MainWindow
, which has visibility of both the signal and the slot objects --- useconnect()
to connect the signal to the slot.
The thread emits a signal with a value parameter at suitable times. The
connect()
causes the slot to be called with that parameter, which it uses to update the progress bar correspondingly. - Make
-
@TheAlmeida
I am @JonB, the author of thatfindMainWindow()
Python function. You have copied it correctly, but you are not using it in the right place/way.However, you have no need of it here.
- Make
updateProgressBar()
acceptval
as a parameter. - If your flavor of PyQt/PySide requires it, mark that method as a slot.
- In your thread define a signal, which takes
val
as a parameter. - And there
emit()
the signal, with a suitable value forval
, as progress is made through the "calculation". - Somewhere --- probably in your
MainWindow
, which has visibility of both the signal and the slot objects --- useconnect()
to connect the signal to the slot.
The thread emits a signal with a value parameter at suitable times. The
connect()
causes the slot to be called with that parameter, which it uses to update the progress bar correspondingly.@JonB Hi, thank you for your reply, but how would you suggest I emit the signal from outside the thread the GUI is running? Your answer makes perfect sense but I am failing to execute it.
I am trying to define the connection inside the__init__
of theUi
and the signal inside theinterface
running on the thread but where do I do the place theemit()
? In the thread on a 1sec timer for example? - Make
-
@TheAlmeida said in Updating QProgressBars running on thread with values from main:
How how I be able to call updateProgressBar() from outside the thread?
Using https://doc.qt.io/qt-5/signalsandslots.html
The thread calculating the values will emit a signal and pass current value as signal parameter. This signal will be connected to a slot in your GUI thread in your UI class. In this slot you will update the progress bar.@jsulm Thank you for your input, it seems (at least for my small programming brain) that you and JonB suggested the same course of action(?) but how can I define the signal outside of the UI class? I am sorry for such dumb and most likely very basic questions
-
@JonB Hi, thank you for your reply, but how would you suggest I emit the signal from outside the thread the GUI is running? Your answer makes perfect sense but I am failing to execute it.
I am trying to define the connection inside the__init__
of theUi
and the signal inside theinterface
running on the thread but where do I do the place theemit()
? In the thread on a 1sec timer for example?@TheAlmeida said in Updating QProgressBars running on thread with values from main:
but where do I do the place the
emit()
? In the thread on a 1sec timer for example?Well, yes, in principle that works. But it makes little sense. If all you wanted was to update a progressbar once per second you wouldn't need a thread and you could just have a timer in the UI thread to do the updates.
Presumably the reason for your thread is that it is doing some operation which takes quite some time to complete, and you want it to signal its progress to completion so that the UI thread can update a progressbar to show it. Else I don't know what your thread is for/doing.
Suppose your thread has a loop doing doing stuff which takes time. First you must declare the signal in the thread class. Look up how you do that in PyQt, I think it's something like a "class-level variable marked with
@pyqtsignal
attribute". Then in the thread class you might have something like:class Something: @pyqtsignal progress(val: int) def doTheWork(self): for i in range(100): self.doSomethingWhichTakesABitOfTime() self.progress.emit(i)
So this would emit a signal with parameter
0..99
as it marched through its work. The main window class would haveconnect()
ed that thread signal to its slot to update the progressbar with the value sent in the signal. -
Initially, I cringed a little: One of the most important things of Qt is that the GUI needs to run in the main thread. A proper implementation would launch the "main code" inside a separate thread instead of the GUI. It seems that you don't have any problems with that in general (you do see widgets in general and can interact with them?). This could be related because in the regular Python interpreter all threads are running inside the same OS thread.
While writing my reply, @JonB already answered you question about slots.
In C++ there is also a way to use
QMetaObject::invokeMethod
to put a call to your updateProgressBar into the GUI's event loop. I am not sure how (or if) this translates to Python. -
Initially, I cringed a little: One of the most important things of Qt is that the GUI needs to run in the main thread. A proper implementation would launch the "main code" inside a separate thread instead of the GUI. It seems that you don't have any problems with that in general (you do see widgets in general and can interact with them?). This could be related because in the regular Python interpreter all threads are running inside the same OS thread.
While writing my reply, @JonB already answered you question about slots.
In C++ there is also a way to use
QMetaObject::invokeMethod
to put a call to your updateProgressBar into the GUI's event loop. I am not sure how (or if) this translates to Python.@SimonSchroeder said in Updating QProgressBars running on thread with values from main:
This could be related because in the regular Python interpreter all threads are running inside the same OS thread.
I don't know if this relevant to anything here, but where do you get this from? Could you provide a reference, please?
This is nothing like my understanding of Python threading. So far as I understood, Python threads absolutely are separate OS threads? Else there would be no point in Python having threading?
What is the case, quite unlike a C++ program, is that threads share one common GIL, which means that when threads execute certain Python statements they will get blocked on this shared resource, because only one instance of the GIL can run (in a single main Python thread) at any one instant. To a certain extent this can serialize Python multi-thread behaviour, at certain execution stages depending on the operation performed. But that is quite different from what you have written.
-
@SimonSchroeder said in Updating QProgressBars running on thread with values from main:
This could be related because in the regular Python interpreter all threads are running inside the same OS thread.
I don't know if this relevant to anything here, but where do you get this from? Could you provide a reference, please?
This is nothing like my understanding of Python threading. So far as I understood, Python threads absolutely are separate OS threads? Else there would be no point in Python having threading?
What is the case, quite unlike a C++ program, is that threads share one common GIL, which means that when threads execute certain Python statements they will get blocked on this shared resource, because only one instance of the GIL can run (in a single main Python thread) at any one instant. To a certain extent this can serialize Python multi-thread behaviour, at certain execution stages depending on the operation performed. But that is quite different from what you have written.
@JonB said in Updating QProgressBars running on thread with values from main:
What is the case, quite unlike a C++ program, is that threads share one common GIL, which means that when threads execute certain Python statements they will get blocked on this shared resource, because only one instance of the GIL can run (in a single main Python thread) at any one instant. To a certain extent this can serialize Python multi-thread behaviour, at certain execution stages depending on the operation performed. But that is quite different from what you have written.
I am not a Python expert. So, probably you are right.