PyQt GUI not responding
-
Hello dear users, i made very simple test code where i have button and this button is connected to function that is just counting to 50 000 000. And when it is done i want to set label text to Done. Everything is ok but while this function is counting my program is not responding. How can i solve this issue ?
Also i noticed this same issue happens to me when i use time.sleep( ). While my program is "sleeping" my QUI is not responding. What i am missing guys ?
This is just example, i need to use this in better code.
Thank you guys.
-
Hi,
You are blocking the event loop.
If you have a long lasting operation you should consider moving it to a thread.
-
@Denni-0 this is an excellent explanation, and I'm sure I'm having the same issue. I read this to mean that if we put something in our QtWidget object code like
new_data = queue.get()
or
while(mode=="running"): new_data = daq.read_channels() time.sleep(0.1)
...then we will see a behavior like the GUI stops responding. I've tried my app both ways above with same result. So my question is, if we have another process that is doing something like sampled data from an external process at say once every 100 ms, what is the best way to do a periodic read of a process to process queue or pipe from within the QtWidget code? I will have only one process producing and one process consuming data in each channel, so I though using a queue would be simplest, but I'm not sure how to create an event when there is new data available in the queue for the QtWidget code to read or to implement a periodic event from within the QtWidget code that triggers reading from the queue. I suppose if I could implement a timer based event to run a method in my QtWidget, I could use:
new_data = queue.get_nowait()
to get the data from the queue without blocking.
-
@MatCauthon
You have 3 possible approaches. It will depend on just how you are communicating between your Qt process and the external process which you say collects the data.-
If you use Qt sockets, or
QProcess
with the sub-process writing to stdout/stderr, you will get a Qt signal which you can put a slot onto, and that will be called when new data arrives. -
If new data arriving does not trigger some kind of event, you can use a
QTimer
to poll a (non-blocking) function (like yourget_nowait()
) and act on that if new data. -
You can use a thread to read new data (blocking is OK here), and
emit
a signal from the thread when it gets new data which your main UI thread has a slot on.
#1 & #2 are simpler as they are asynchronous and can be done in your main UI thread. #3 may be required but requires more work for threading.
If you have not already done so, you need to read through https://doc.qt.io/qt-5/signalsandslots.html as this is fundamental to how Qt works and your question.
You could implement some of the above with Python calls instead of Qt ones if you prefer, but the principles remain the same.
-
-
@JonB thanks for the options. I think I like option 3. As I understand it, option 2 will tie up the processor with the continuous polling. I did try to use my own signals from the class that was sending the data, but as I understand it, every object that generates a signal needs to be a QObject, and I think that will be more work for me than option 3 actually.
It seems to me I can have my QtWidget interface running in one process, my data updating in another process, put new data in a queue, then run:
new_data.queue.get() new_data_signal_emit()
in a thread from my QtWidget. It may take me some time to rearrange things, but if I get it working, I'll post the working example back here.
-
@MatCauthon
I am fine if you wish to do it via option #3 thread. HoweverAs I understand it, option 2 will tie up the processor with the continuous polling.
No, else I wouldn't have suggested it! :) In option #2 you would use a
QTimer
to trigger the poll calls.QTimer
only runs on a repeating interval and does the poll then; the rest of the time your UI thread is doing whatever/nothing, as necessary.Generating a signal does require a class which derives from
QObject
, and you raise the signal withemit
. However, that does not require that some existing class which is concerned with your data exchange be re-written to includeQObject
among the classes it inherits. You can always write a wrapper class, which does inheritQObject
, and has your existing communicating instance as a member. This is know as encapsulation.Just wanted you to be aware of options, As I said, going with a thread is fine --- so long as you get your threading code right! :)