Solved QLabel stops updating (setText does not work)
-
Edit:
Short Answer: My problem was using threads for updating GUI elements. Using QThread and pyqtSignals solved my issue.I am using PyQt5.
I designed a gui which gets necessary data values from a running thread and displays the values in the QLabel objects of the gui.
The data obtained are changing continuously and very frequently. I use another thread for updating the label texts continuously.
The problem is, this operation works well in the beginning but after a short time, labels stop showing the real most recent value, the gui just stops displaying new values, the latest value before the breakdown remains until I halt the program.
The thing is, I am sure I feed the setText method the correct (most recent) values, and when I print thelabel.text()
values after trying to set the text, it prints the correct values here too. So I think labels actually have correct text but GUI does not display them after a short time, it just stops updating.What is the soluttion for this? I need to keep updating the values in the gui for an indefinite amount of time. Any help is appreciated. If necessary, my code for updating is below:
def updateValues(self): while self.condition: self.values_dict = self.values_listener_func() print("GUI VALUES DICT:\n", self.values_dict) # values are printed correctly even after gui update stops for title in self.values_dict: self.value_labels[title].setText(str(self.values_dict [title])) print(self.value_labels[title].text()) # values are printed correctly even after gui update stops # which means label texts are actually changed but not displayed QtWidgets.QApplication.processEvents() # does not fix not updating problem time.sleep(self.wait_time)
-
@appleren
You need to write code differently for an event-driven UI like Qt. You must not used a call liketime.sleep(self.wait_time)
. Nor should you have awhile self.condition
This blocks Qt's event loop which services the UI. That is why you do not see your updates. And try not to useQtWidgets.QApplication.processEvents()
either, it can be useful but is usually indicative of a design problem.If you need a timer you would use a QTimer.
I designed a gui which gets necessary data values from a running thread and displays the values in the QLabel objects of the gui.
The data obtained are changing continuously and very frequently. I use another thread for updating the label texts continuously.
I don't know if your thread is necessary or if its code is good. If you need to do this you must be using signals from the thread. You cannot and must not directly update a GUI element from another thread, if that is what you are doing.
-
Hi and welcome to devnet,
Beside the good point of @JonB, do not access GUI elements from a different thread.
-
@JonB
First of all thanks for the answer. Actually I do not have much experience with PyQt, so thanks for the warnings and it would be great if you could explain more:- The "updateValues " function I wrote above is a thread's target function, thats what I meant by "I use a thread for updating the label texts continuously". "values_listener_func" is the function for obtaining the most recent data, so it returns different values in each iteration. I have a main class for the GUI and I start this thread within the class to update the label values.
Is this a problem, if yes what approach should I use? - I can look into QTimer and use it instead of sleep function, but how is using "while condition" wrong and how can I correct it?
Thanks.
- The "updateValues " function I wrote above is a thread's target function, thats what I meant by "I use a thread for updating the label texts continuously". "values_listener_func" is the function for obtaining the most recent data, so it returns different values in each iteration. I have a main class for the GUI and I start this thread within the class to update the label values.
-
@appleren said in QLabel stops updating (setText does not work):
The "updateValues " function I wrote above is a thread's target function
I have a main class for the GUI and I start this thread within the class to update the label values.
If you mean: you run
updateValues()
in a non-main-UI thread, then that is your problem. Are you saying this? That violates that you must not update or even access the UI from any thread other than its own.If you need a thread to cause the UI to update, you have to send signals with the necessary data and connect the main UI thread with a slot to do the UI updates required.
-
@JonB
Yes, I was using a non-main thread (threading.Thread object) to update the labels. The thing is, I use the exact same method for another similar task (updating an image on the GUI continuously), and that worked great, without any problems. My aim is to make the same thing work with the "values labels". Then I will run these two operations, one will update the values of the labels and other one will update the image continuously. These work independent from each other. Thats why I was using threads.Now I understand that using a non-main thread to update GUI is not healthy. But I have some questions:
- Why does the thread which updates the image work (image is displayed with a QLabel too), but the other thread which updates multiple labels does not work properly (it actually works for a few seconds, then it breaks down)?
- From your answer I understand that I should use
QThread
instead of athreading.Thread
and use signals. https://stackoverflow.com/a/9964621/9181308 would a solution like this suffice, with awhile True
loop in the thread class'run
method (because values change constantly)? And is usingtime.sleep
in the loop still dangerous even though the non-main thread does not access GUI elements directly (I will use a QThread and using signals to use main thread to update the labels)?
-
@appleren
I hoped you would get an answer from someone other than me as I am not a great fans of threads. But not so far.If a non-UI thread worked for you at updating the UI you were "lucky". So you might get away in some circumstances but not others for no discernible reason.
At the time I first saw your
while True
, and yourtime.sleep()
, I did not know that was in a thread. That makes it only potentially more acceptable. It depends what yourvalues_listener_func()
does. It's true now that you won't block the GUI thread, but nonetheless you don't want your thread to chew up CPU time.The Qt way of doing this would be to have a
QTimer
ticking and you read the data each time that expires, rather than a blocking loop with a sleep. Using Python with Qt I do not know what the interactions between Qt threads, Python threads andtime.sleep()
is.The other possibility is: are you sure you need a thread at all? A large number of newcomers jump straight into threads when they are not necessary. If you just had the main GUI thread with a
QTimer
used to pull the next batch of samples which have arrived every so often, would that simply achieve whatever you are doing now? -
If you need to generate images for your labels in a different thread, then you might want to check the Mandelbrot Example.
Do not use
while(true)
, always have a mean to exit your mane loop cleanly. -
@JonB @SGaist
Thanks for all the help.Using the approach I mentioned above (Using signals with QThread: https://stackoverflow.com/a/9964621/9181308) solved my problem.
I still do not get this:
- Why does the thread which updates the image work (image is displayed with a QLabel too), but the other thread which updates multiple labels does not work properly (it actually works for a few seconds, then it breaks down)?
It was like this every single time I ran the program, in a few different computers, so I don't know how it is "luck", but I am not concerned with this right now.