Qt Thread Slot Signal
Lets explain it more, Each thread created by Qt (including main thread and new threads created by QThread) have Event loop, the event loop is responsible for receiving signals and call aproporiate slots in its thread. Qt's signals and slots mechanism ensures that if you connect a signal to a slot, the slot will be called with the signal's parameters at the right time. Signals and slots can take any number of arguments of any type. They are completely type safe.
I have a Qt 'signal-slot connection'.
I want to test the class with the signal
The class with the slot is mocked/stubbed.
Now I would like to verify that when I emit signal, the slot is
called.
I wonder if it is possible to use gmock to verify this.
// class Tx contains the signal, which is protected so I inherit
// to get access to it
class Test : public ::testing::Test, public Tx
{ public:
MockRx *rx;
void SetUp()
{ rx = new MockRx;
rx->rxsignal(); // Just to see it compiles
rx->gmock_rxsignal(); // Compiles, gmock generated function
QObject::connect(this, SIGNAL(txsignal(), rx, SLOT(rxsignal()));
}
void TearDown() { delete rx; }
};
TEST_F(Test, Signal)
{
EXPECT_CALL(*rx, rxsignal()).Times(1).WillOnce(Return());
this->txsignal();
}
class MockRx : public QWidget
{ Q_OBJECT
public:
MOCK_METHOD0(rxsignal, void());
};
When running, I get the error:
QObject::connect: No such slot as RxMock::rxsignal()
If I remove Q_OBJECT in class MockRx, I get: No such slot as
QWidget::rxsignal()
I also tried to QObject::conntect(... SLOT(gmock_rxsignal()));
which gives No such slot as RxMock::rxsignal()
If I replace the MockRx class with a class Rx with rxsignal()
everything works.
I wonder why QObject::connect cannot find RxText::rxsignal() when
- it works if I replace with a regular class Rx instead of class
MockRx
- rx->rxsignal() can be called
Is gmock doing something under the hood that prevents this?
Hope someone can help, thanks a lot
Paul
Effective Threading Using Qt
Over the years using Qt I’ve seen a lot of difficulty using threads with Qt.Threads can be difficult and Qt provides a lot of ways to make threads easy towork with. Still basic / direct / low level threading (I’ll just call thisbasic) is often seen as difficult with Qt. It really isn’t though.
There are three main ways I’ve seen people handle basic threading in their Qtapplications. The first is using system threads, either pthread or Windowsthreads. I don’t like this approach because you’re basically writing a portablethread library. You can use an already built portable thread library but whyuse a non-Qt solution when Qt already provides portable threading.
The two other approaches are defined by Qt’s QThreaddocumentation. 1) use a QObject worker. 2)subclass QThread and reimplement the run function.
I like using a QObject worker and I think this is the best (and easiest)approach. That said, I don’t like how Qt’s documentation explains this. It usestwo QObject classes to handle this. A variation of this approach is what I’mgoing to demonstrate.
The other documented approach is subclassing QThread. This isn’t a horribleidea but I don’t think it’s the cleanest approach. It also ties thefunctionality to a thread making code reuse low in this case. You could getaround this limitation by putting the functionality in a separate class but nowyou have two classes and the thread subclass really isn’t necessary when usingthe QObject worker approach.
This is a very simple example that demonstrates two types of workers. One takesarguments and runs until it’s task is finished. The second on runs until it’stold to stop. The second worker could take arguments if you need it to. Forthis example the workers both just increment a count every second and send theresult to a QMainWindow
for it to be displayed.
Here is all the code that goes into the example which will have the main partsexplained after:
main.cpp
mainwindow.h
mainwindow.cpp
Qt Emit Signal From Thread
mainwindow.ui
countworker.h
countworker.cpp
infinitecountworker.h
infinitecountworker.cpp
portablesleep.h
threader.pro
CountWorker
This is a very simple object that increases a count every second. The start andend are set as part of the constructor. Setting up the object using theconstructor makes sense but it’s also because to start the worker we can’t passany arguments. This is a limitation of this method that the worker in Qt’s docsdoes not have. That said, I think it’s fine to use the constructor for argumentpassing. This method doesn’t reuse workers (not a limitation) so it’s fine.Once the worker finishes it’s work is all done and it stops.
InfiniteCountWorker
This worker is very similar to the count worker. It’s to demonstrate infinitetasks unlike CountWorker which demonstrates finite tasks.
The key to his worker is the stopWork slot. Calling this sets sets a flag tolet the worker’s doWork function to exit.
*Workers
Both workers have two signals in common, updateCount
and finished
.UpdateCount
simply sends the current count off. The MainWindow
connects anduses this to, well, update the count. finished signals that the worker is done.This is used to stop the thread and takes care of cleanup. The finished signalcan have multiple overloads. For example to send off a result in addition tosignaling that the worker is finished. You must have a no argument signal forcompletion with this method. You could use a different signal name instead ofoverloading “finished” if you want.
One thing to think about is thread synchronization. Well, with this workermethod you don’t need to worry about it. Initialization of parameters happensbefore the worker is moved to the thread and before the thread is even started.All passing (such as updateCount) happens using signals and slots. When passingdata between threads using signals and slots Qt handles thread synchronizationfor you. The stopWork
function is called via a signal so the function runs onthe thread the work is running on between iterations of the while loop. Sothere is no need to wrap m_running
in a mutex or other synchronizationtechniques.
It might be confusing that I said the stopWork
function happens betweeniterations of the while loop in InfiniteCountWorker. This is because of theqApp->processEvents
call. Without this, the signal to initiate the stopWork
slot won’t be delivered until after the while loop finishes. Which isimpossible in infiniteCountWorker
. Remember the thread is a single thread andeverything running on it is single threaded. The while loop will block anythingelse in the object from running unless you use qApp->processEvents
to allowsignals and slots to process. In the case of the CountWorker
qApp->processEvents
isn’t really necessary since there aren’t any signalsthat are delivered to it. processEvents
is only necessary for signals (fromoutside of or within the thread the worker is running) to initiate slots in theworker. It is not necessary for the worker to send off a signal (likeupdateCount
) to another thread that it’s connected to.
If you haven’t realized by now that with the worker method (my variation orQt’s) the worker has an event loop. Subclassing QThread
only does in somecases (see the documentation for when). You get signals into and out of workerwithout any additional code (except for a single processEvents
call). You getthis essentially for free by using a worker!
MainWindow
The workers aren’t really useful unless they can be started on a thread. That’swhat the MainWindow
in this example is for. The startCount
andstartInfiniteCount
functions are the main thing to look at.
startCount Breakdown
The thread and worker are created on the heap. The worker is initialized heretoo.
Put the worker on the thread so the worker will be run on the thread we createdinstead of the UI thread.
This is how the worker is started when the thread is started.
Connect all the finished signals. When the worker finishes the thread will bestopped with quit()
, the worker will be deleted via deleteLater
. Qt handlesdeletion and takes care of it when safe. This is another advantage of usingQObject
workers. Also, when the worker finishes the countFinished
functionis called so anything in the MainWindow
that needs to happen when the workeris finished is run. Again, you could have a second finished signal (overload)that passes result data back to the MainWindow
. Finally, when the threadfinishes (because of the worker’s finished signal calling the threads quitslot) it will be deleted by deleteLater
. The deleteLater
slots mean wedon’t need to track the the worker or thread pointers and worry about manuallydeleting them.
deleteLater
is very useful. We have finished connected to multiple slots.Deleting a QObject
when there are events pending or within a signal handler(slot) can lead to a crash. deleteLater
ensures that this won’t happen bywaiting until all events are delivered. Also, you can’t delete a QObject
froma thread so this also ensures the UI thread that created the worker and threadis where they are deleted. In this example this isn’t a concern but it’s niceto know that it won’t become one.
We don’t need to worry about something like dangling connections because when aQObject
is deleted it automatically disconnects all signals and slots (not inall cases but that will be covered later).
Here the count will be updated on the MainWindow
as it’s incremented in theworker. Again, there is no manual thread synchronization necessary because Qthandles this as part of it’s signal and slot system.
The last line to worry about actually starts the thread which in turns startsthe worker.
startInfiniteCount Breakdown
This function is very similar to startCount
. The only difference is one line.
The MainWindows
button that will stop the InfiniteCountWorker
is connectedto the InfiniteCountWorker
’s stop function.
PortableSleep
portablesleep.h is a header only cross platform sleep class. This is how wecreate the one second delay when counting. With Qt5 you can (should) useQThreads
msleep
or sleep
public static functions instead. Qt4 on theother hand defines these functions as protected static functions. So there isno clean way to use them aside from creating a QThread
subclass and exposingthem. Note that this example is not limited to Qt4. This example will runand work with both Qt4 and Qt5. A little bit of thought is all that’s needed toachieve this.
This information isn’t specific to threading but could be very useful to reallyget the most of the worker concept.
This example only uses int. This is a type that can be used with Qt’s signalsand slots as well as many other types such as QString
. There are many complextypes like your own classes or even QMap<QString, QString>>
cannot be usedimmediately with signals and slots.
I say immediately because any class with a public constructor, copy constructorand destructor can be registered with Qt using qRegisterMetaType
. This allowsthe object type to be used with signals and slots. For example you can use thefollowing in the MainWindow
’s constructor to allow a complex type to be used.
The reason we need constructor, copy constructor and destructor is because whennecessary Qt will create a copy of an object and pass the copy to a slot. Inthe case of passing objects between threads using signals and slots a copy willbe passed to the slot. Remember primitive types like int and pointers arealways copies. Complex types may or may not be copies depending on thesituation.Realize that if you are passing a pointer between threads you will need manualsynchronization. If you’re allowing copies to be passed then you don’t need toworry about synchronization.
You’ve probably noticed that in this example the old style SIGNAL
and SLOT
macros were used in the connect functions. This is on purpose because itsupports more compilers. Also, as mentioned, the example code works with bothQt4 (it is still used) and Qt5. I don’t really like new syntax provided byC++11 either. There are too manynegatives for my liking. In thiscase the fact that automatic connection disconnection isn’t support is prettybig. Nor is overloading signals very clean which makes using multiple versionof finish not a friendly. But this is my personal preference and not requiredfor this threading method.
Contrary to popular belief Qt provides very powerful basic threadingcapabilities. I say basic because I didn’t even mention Qt Concurrent whichprovides a lot of high level threading support. Oh and QThread
pool wasn’tmentioned. None of the thread synchronization objects like QMutex
weredescribed either.
Qt Thread Safe Signal Slot
Even with all of the threading objects that Qt provides it’s still really easyto use threading with QObject
workers. For most people this is going to beenough for offload some functionality and keep the UI from blocking. While thisis the method I use quite often there are plenty of other ways. I just happento find this to be the cleanest and easiest.