Cancel QThread in PyQt5 - qt

I have a GUI in PyQt5, which starts a QThread that reads from a serial port. The thread does quit, when it read all the data, but I want to be able to stop it when I click on a stop button. How do I do that? Here is the basic code:
# ...
class Worker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(list)
def __init__(self):
QObject.__init__(self)
self._Reader = Reader()
self._Reader.progress = self.progress
self._Reader.finished = self.finished
def run(self):
self._Reader.read()
class Ui(QtWidgets.QMainWindow):
# ...
def startClicked(self):
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker.finished.connect(self.workerFinished)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.progress.connect(self.reportProgress)
self.thread.start()
def stopClicked(self):
# How do I stop the thread?
pass

when managing threads you can do, as states in the doc here: https://doc.qt.io/qt-5/qthread.html
You can stop the thread by calling exit() or quit().
https://doc.qt.io/qt-5/qthread.html#exit
exit:
Tells the thread's event loop to exit with a return code.
After calling this function, the thread leaves the event loop and returns from the call to QEventLoop::exec(). The QEventLoop::exec() function returns returnCode.
By convention, a returnCode of 0 means success, any non-zero value indicates an error.
https://doc.qt.io/qt-5/qthread.html#quit
quit:
Tells the thread's event loop to exit with return code 0 (success). Equivalent to calling QThread::exit(0).
This function does nothing if the thread does not have an event loop.

I assume that you read data in some data processing loop. If this assumption is wrong, then the following is not valid, of course.
You cannot call secondary thread's quit() directly from the main thread and expect that the secondary thread will process it immediately and quit the thread. The reason is that the thread is busy reading the data in the data processing loop. So you need to break the data processing loop in the secondary thread to make the event loop idle.
(Btw. do not confuse the data processing loop with the event loop. Data processing loop is the one which you have written yourself to read data from the port. The event loop is the loop created by Qt automatically after you called QThread::start() and which is processing events, signals and slots in the secondary thread. This event loop is blocked while your data processing loop is running.)
In order to break the data processing loop, you need to do two things:
call QThread::requestInterruption() from the main thread as response to some "Abort" button having been pressed (do not worry about thread safety, requesting interruption is thread safe/atomic)
within the loop in the secondary thread you need to periodically check QThread::isInterruptionRequested(), and if this returns true, then break the loop and emit worker's finished() signal
Once you broke from the data processing loop in the secondary thread, the event loop in the secondary thread becomes available for processing signals sent from the main thread.
I can see in your code that worker's finished() signal is connected to QThread::quit(). So emitting finished() from the secondary thread (after you broke from the data processing loop) will call thread's quit() which will be processed by the secondary thread's event loop (which is now idle) and it will quit the event loop and subsequently the thread and if you have connected everything correctly it will delete the worker and the thread. (though I have not checked this part of your code)

Related

How Qt Handle Events and Signal in Same EventLoop

I couldn't understand how qt handle events (e.g timer event, socket event etc.) and signals in same event loop.As I understand,timer and socket events are handled via select system call(in Unix like OS).
How an event loop handle signals while sleeping because of select system call.
In Qt, signals are used to call slots. When you emit a signal, there are, roughly speaking, only 2 options for calling the corresponding slot:
Direct slot call. This can be thought of as replacing a line with a signal emitting by a line with just a slot call. An event loop is not used to process this signal itself.
Delayed slot call. In this case, the signal will be converted to an event, and the event will be posted to the receiver event loop (the event enqueues in the event loop of the thread the receiver object is living in). From now on, for the processing receiver event loop, it makes no difference whether it was a signal or an event. The event will be picked up by the event loop and will cause the slot invocation sometime later.
From Qt doc: https://doc.qt.io/qt-5/signalsandslots.html#signals
When a signal is emitted, the slots connected to it are usually
executed immediately, just like a normal function call. When this
happens, the signals and slots mechanism is totally independent of any
GUI event loop. Execution of the code following the emit statement
will occur once all slots have returned. The situation is slightly
different when using queued connections; in such a case, the code
following the emit keyword will continue immediately, and the slots
will be executed later.
As for understanding an event loop, an event loop is just a loop which process one event from an event queue on each iteration.
In short, this can be represented as follows:
QQueue<QEvent> eventQueue; // Events (and pending slot calls as well) are added to this queue
...
// How an event loop works (schematically):
while(event = eventQueue.dequeue())
{
do_what_the_event_wants_or_ignore_it(event);
}
Read also https://wiki.qt.io/Threads_Events_QObjects

when the event loop starts in Dart and how the event queue works

The first question is when the event loop starts ?
I read in a site that it's start after the main method
but why when we try something like this
main()async {
Future(()=>print('future1'));
await Future(()=>print('future2'));
print('end of main');
}
//the output is :
//future1
//future2
//end of main
in this example the event loop start when we use the await keyword and
after the event loop reaches the future2 it's paused ?
or i am wrong :(
The second question is how the events is added to event queue
if it's FIFO why in this example the future 2 is completed before
future 1
main(){
Future.delayed(Duration(seconds:5) , ()=>print('future1'));
Future.delayed(Duration(seconds:2) , ()=>print('future2'));
}
The event loop run when there is nothing else running (e.g. main method is done, you are waiting for some future to complete).
Your example makes sense because the first line puts an event on event queue so now the first item in the queue is "print('future1')". In the next line, you are putting another event on the queue which calls "print('future2')" and now you await for this event to be done.
Since your main method is not waiting for something then the event loop is going to be executed. Since the first event on the queue was "print('future1')" then this is going to be executed first. But since the main method is still waiting for the future "print('future2')" to be complete then the event loop takes another event to be executed which are going to be "print('future2')".
Since this event was the one the main method was waiting for (and there is no more event on the event queue) then main() are going to run the last call "print('end of main')".
In your next example, you think that Future and Future.delayed are the same which it is not. With Future.delayed there are not going any event in the event queue before. Instead, there are running a thread outside the VM which knows when the next timer should trigger which ends up putting an event on the queue. So the event is only being put on the event queue when the timer has been expired (and therefore, the future2 are going to be executed first).

Direct connection and event loop [duplicate]

This question already has answers here:
Qt signals (QueuedConnection and DirectConnection)
(3 answers)
Closed 8 years ago.
I have read the doc of Qt and have the following question:
if I have:
class_A::on_button_click(){
...do some things...//no signal emit this part
emit signal_A();
}
class_B::on_signal_received(){
...do some things...//no signal emit this part
}
...
connect(class_A_object,SIGNAL(signal_A()),class_B_object,on_signal_received);
All the things here are in the main thread,
now when I click the button and trigger the first function,
the program will be executed from the first line of
class_A::on_button_click()
until the last line of
class_B::on_signal_received()
and during the process nothing else in the main thread will get the chance to be executed, is this correct?( "...do some things..." part don't emit any signals)
In other words, when is the moment that the control return to the event loop?
After finishing
class_A::on_button_click()
or
class_B::on_signal_received()
?
When your signal and slot are in the same thread (as mentioned by user3183610) your signal will be direct connection (the default for same-thread). This effectively runs similarly to a function call. The signal is not queued, but instead the slot that the signal is connected to executes immediately - so you can think of it as a direct function call to the slot.
You can, however, change this behavior by using the Qt::QueuedConnection optional parameter at the end of your connect call:
connect(class_A_object,SIGNAL(signal_A()),class_B_object,on_signal_received, Qt::QueuedConnection);
This will force the use of the queue, your signal will be queued in the event loop and then other pending signals will be executed in order (this is often more desirable then DirectConnection because you can more easily guarantee the order of events). I tend towards to use of queued connections for this reason (though I believe direct is very slightly more efficient).
So for your code there is no return to the event loop until after on_button_click(). During on_button_click() you emit the diret signal signal_x() and immediately on_signal_received() is called (by-passing the event loop), when this finishes it returns back to on_button_click() - just like a function call would :)

Stop QThread to execute

I am trying to implement timeout in my QT Application. I have used QThread to perform the operation (task on which timeout is required) and have used QElapsedTimer to count elapsed time waiting to perform the operation. Below is the code snippet
QElapsedTimer timeoutTimer; // Timer to count the elapsed time waiting for the operation to complete.
long timeoutInterval=10000
MyThread mThread(); // QThread implementation
timeoutTimer.start();
mThread.start();
while((timeoutTimer.elapsed() < timeoutInterval) && mThread.isRunning()){
sleep(5);
}
if(mThread.isRunning()){
mThread.terminate();
}
Now, if task is not completed and timeout happens I get "Destroyed while thread is still running" and application gets crashed.
I tried to call terminate() function of QThread but it is working on Windows however on Linux I get segmentation fault.
You have mentioned that quit() doesn't work for your thread, so I suppose you have reimplemented QThread::run method and doesn't use event loop in your implementation. As the documentation says:
Subclassing QThread is unnecessary for most purposes, since QThread
provides fully-functional thread management capabilities. Nonetheless,
QThread can be subclassed if you wish to implement advanced thread
management. This is done by adding new member functions to the
subclass, and/or by reimplementing run(). QThread’s run() function is
analogous to an application’s main() function — it is executed when
the thread is started, and the thread will end when it returns.
Note: Prior to Qt 4.4, the only way to use QThread for parallel
processing was to subclass it and implement the processing code inside
run(). This approach is now considered bad practice; a QThread should
only manage a thread, not process data.
So, do not subclass QThread. You can just use the standart event loop and signal-slot system to use your thread. You also can use more high-level interface such as QRunnable if it can be done for your task.
If you are sure that you want to reimplement QThread::run and eliminate advantages of event loop, than you should take care of stopping your thread manually. For example, you can use some boolean flag bool need_to_stop and check its value periodically as the thread runs. When you decided to stop the thread, set the flag's value to true and then call QThread::wait(). When QThread::run is finished because of the flag's value, your thread will be stopped and wait() will return. Note however that you can't simply use one flag simultaneously from both your new thread and GUI thread. You need some synchronization mechanism like QMutex. So, this thing is overcomplicated. Just don't subclass QThread if you don't want to do something really low-level.
Try using void QThread::quit () as mentioned in documentation
Tells the thread's event loop to exit with return code 0 (success). Equivalent to calling QThread::exit(0).
This function does nothing if the thread does not have an event loop.

Signal handling

The case is: I register a signal handler with signal function.
The question:
Is handler always called independently of the process state? (I mean its stopped, etc.). What happens there?(dependently of the state)
Are handler functions registered as some "special functions" by system (i.e. when handeler runs other signals are not recieved and are put into the stack or smth like that. Or maybe they are simply ignored.) If not, imagine that when handler strats, process gets another signal, then this handler is called again inspite of "the first" hasn't completed its task yet and so on.
"While a process is stopped, any additional signals that are sent to the process shall not be delivered until the process is continued, except SIGKILL (...) The default action for SIGCONT is to resume execution at the point where the process was stopped, after first handling any pending unblocked signals." (Unix standard, section Signal Concepts.)
Ordinarily, nothing special happens. When a signal X is caught while in a signal handler for Y, execution is simply transferred to the handler for X, after which the handler for Y resumes execution.
The following program demonstrates this behavior. raise(sig) sends a signal to the calling process (it's like kill(getpid(), sig)).
void hello(int unused)
{
printf("Hello, ");
raise(SIGUSR2);
printf("!\n");
}
void world(int unused)
{
printf("world");
}
int main()
{
signal(SIGUSR1, hello);
signal(SIGUSR2, world);
raise(SIGUSR1);
return 0;
}
This is "safe" because a process will accept signals only from processes with the same user ID (or root), so you can only shoot yourself in the foot this way.

Resources