Qt Signal Slot Different Thread
A short history
Long long ago, subclass QThread and reimplement its run() function is the only recommended way of using QThread. This is rather intuitive and easy to used. But when SLOTS and Qt event loop are used in the worker thread, some users do it wrong. So Bradley T. Hughes, one of the Qt core developers, recommend that use worker objects by moving them to the thread using QObject::moveToThread . Unfortunately, some users went on a crusade against the former usage. So Olivier Goffart, one of the former Qt core developers, tell the subclass users: You were not doing so wrong. Finally, we can find both usages in the documentation of QThread.
The receivers of signals are called Slots in Qt terminology. A number of standard slots are provided on Qt classes to allow you to wire together different parts of your application. However, you can also use any Python function as a slot, and therefore receive the message yourself. QtCore.SIGNAL and QtCore.SLOT macros allow Python to interface with Qt signal and slot delivery mechanisms. This is the old way of using signals and slots. The example below uses the well known clicked signal from a QPushButton. The connect method has a non python-friendly syntax. It is necessary to inform the object, its signal (via macro. Signals and Slots Across Threads Qt supports these signal-slot connection types: Auto Connection (default) If the signal is emitted in the thread which the receiving object has affinity then the behavior is the same as the Direct Connection. Otherwise, the behavior is the same as the Queued Connection.' 8BiTs 매일 코딩 홈페이지. (Qt) Cross Thread Signal and Slots - 2 Get Code.
QThread::run() is the thread entry point
From the Qt Documentation, we can see that
A QThread instance represents a thread and provides the means to start() a thread, which will then execute the reimplementation of QThread::run(). The run() implementation is for a thread what the main() entry point is for the application.
As QThread::run() is the thread entry point, it is rather intuitive to use the Usage 1.
Usage 1-0
To run some code in a new thread, subclass QThread and reimplement its run() function.
For example
The output more or less look like:
Usage 1-1
As QThread::run() is the thread entry point, so it easy to undersand that, all the codes that are not get called in the run() function directly won't be executed in the worker thread.
In the following example, the member variable m_stop
will be accessed by both stop() and run(). Consider that the former will be executed in main thread while the latter is executed in worker thread, mutex or other facility is needed.
The output is more or less like
You can see that the Thread::stop() is executed in the main thread.
Usage 1-2 (Wrong Usage)
Though above examples are easy to understand, but it's not so intuitive when event system(or queued-connection) is introduced in worker thread.
For example, what should we do if we want to do something periodly in the worker thread?
- Create a QTimer in the Thread::run()
- Connect the timeout signal to the slot of Thread
At first glance, the code seems fine. When the thread starts executing, we setup a QTimer thats going to run in the current thread's event queue. We connect the onTimeout()
to the timeout signal. Then we except it works in the worker thread?
But, the result of the example is
Oh, No!!! They get called in the main thread instead of the work thread.
Very interesting, isn't it? (We will discuss what happened behined this in next blog)
How to solve this problem
In order to make the this SLOT works in the worker thread, some one pass the Qt::DirectConnection
to the connect() function,
and some other add following line to the thread constructor.
Both of them work as expected. But ...
The second usage is wrong,
Even though this seems to work, it’s confusing, and not how QThread was designed to be used(all of the functions in QThread were written and intended to be called from the creating thread, not the thread that QThread starts)
In fact, according to above statements, the first workaround is wrong too. As onTimeout() which is a member of our Thread object, get called from the creating thread too.
Both of them are bad uasge?! what should we do?
Usage 1-3
As none of the member of QThread object are designed to be called from the worker thread. So we must create an independent worker object if we want to use SLOTS.
The result of the application is
Problem solved now!
Though this works perfect, but you may have notice that, when event loop QThread::exec()
is used in the worker thread, the code in the QThread::run() seems has nothing to do with QThread itself.
So can we move the object creation out of the QThread::run(), and at the same time, the slots of they will still be called by the QThread::run()?
Usage 2-0
If we only want to make use of QThread::exec(), which has been called by QThread::run() by default, there will be no need to subclass the QThread any more.
- Create a Worker object
- Do signal and slot connections
- Move the Worker object to a sub-thread
- Start thread
The result is:
As expected, the slot doesn't run in the main thread.
In this example, both of the QTimer and Worker are moved to the sub-thread. In fact, moving QTimer to sub-thread is not required.
Simply remove the line timer.moveToThread(&t);
from above example will work as expected too.
The difference is that:
In last example,
- The signal
timeout()
is emitted from sub-thread - As timer and worker live in the same thread, their connection type is direct connection.
- The slot get called in the same thead in which signal get emitted.
While in this example,
- The signal
timeout()
emitted from main thread, - As timer and worker live in different threads, their connection type is queued connection.
- The slot get called in its living thread, which is the sub-thread.
Thanks to a mechanism called queued connections, it is safe to connect signals and slots across different threads. If all the across threads communication are done though queued connections, the usual multithreading precautions such as QMutex will no longer need to be taken.
In short
- Subclass QThread and reimplement its run() function is intuitive and there are still many perfectly valid reasons to subclass QThread, but when event loop is used in worker thread, it's not easy to do it in the right way.
- Use worker objects by moving them to the thread is easy to use when event loop exists, as it has hidden the details of event loop and queued connection.
Reference
- http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
- http://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html
- http://ilearnstuff.blogspot.com/2012/08/when-qthread-isnt-thread.html
Build complex application behaviours using signals and slots, and override widget event handling with custom events.
As already described, every interaction the user has with a Qt application causes an Event. There are multiple types of event, each representing a difference type of interaction — e.g. mouse or keyboard events.
Events that occur are passed to the event-specific handler on the widget where the interaction occurred. For example, clicking on a widget will cause a QMouseEvent
to be sent to the .mousePressEvent
event handler on the widget. This handler can interrogate the event to find out information, such as what triggered the event and where specifically it occurred.
You can intercept events by subclassing and overriding the handler function on the class, as you would for any other function. You can choose to filter, modify, or ignore events, passing them through to the normal handler for the event by calling the parent class function with super()
.
However, imagine you want to catch an event on 20 different buttons. Subclassing like this now becomes an incredibly tedious way of catching, interpreting and handling these events.
Qt Signal Slot Different Threads
Thankfully Qt offers a neater approach to receiving notification of things happening in your application: Signals.
Signals
Instead of intercepting raw events, signals allow you to 'listen' for notifications of specific occurrences within your application. While these can be similar to events — a click on a button — they can also be more nuanced — updated text in a box. Data can also be sent alongside a signal - so as well as being notified of the updated text you can also receive it.
The receivers of signals are called Slots in Qt terminology. A number of standard slots are provided on Qt classes to allow you to wire together different parts of your application. However, you can also use any Python function as a slot, and therefore receive the message yourself.
Load up a fresh copy of `MyApp_window.py` and save it under a new name for this section. The code is copied below if you don't have it yet.
Basic signals
First, let's look at the signals available for our QMainWindow
. You can find this information in the Qt documentation. Scroll down to the Signals section to see the signals implemented for this class.
Qt 5 Documentation — QMainWindow Signals
As you can see, alongside the two QMainWindow
signals, there are 4 signals inherited from QWidget
and 2 signals inherited from Object
. If you click through to the QWidget
signal documentation you can see a .windowTitleChanged
signal implemented here. Next we'll demonstrate that signal within our application.
Qt 5 Documentation — Widget Signals
The code below gives a few examples of using the windowTitleChanged
signal.
Try commenting out the different signals and seeing the effect on what the slot prints.
We start by creating a function that will behave as a ‘slot’ for our signals.
Then we use .connect on the .windowTitleChanged
signal. We pass the function that we want to be called with the signal data. In this case the signal sends a string, containing the new window title.
If we run that, we see that we receive the notification that the window title has changed.
Events
Next, let’s take a quick look at events. Thanks to signals, for most purposes you can happily avoid using events in Qt, but it’s important to understand how they work for when they are necessary.
As an example, we're going to intercept the .contextMenuEvent
on QMainWindow
. This event is fired whenever a context menu is about to be shown, and is passed a single value event
of type QContextMenuEvent
.
To intercept the event, we simply override the object method with our new method of the same name. So in this case we can create a method on our MainWindow
subclass with the name contextMenuEvent
and it will receive all events of this type.
If you add the above method to your MainWindow
class and run your program you will discover that right-clicking in your window now displays the message in the print statement.
Qt Emit Signal From Thread
Sometimes you may wish to intercept an event, yet still trigger the default (parent) event handler. You can do this by calling the event handler on the parent class using super
as normal for Python class methods.
This allows you to propagate events up the object hierarchy, handling only those parts of an event handler that you wish.
However, in Qt there is another type of event hierarchy, constructed around the UI relationships. Widgets that are added to a layout, within another widget, may opt to pass their events to their UI parent. In complex widgets with multiple sub-elements this can allow for delegation of event handling to the containing widget for certain events.
Qt Signal Thread
However, if you have dealt with an event and do not want it to propagate in this way you can flag this by calling .accept()
on the event.
Alternatively, if you do want it to propagate calling .ignore()
will achieve this.
In this section we've covered signals, slots and events. We've demonstratedsome simple signals, including how to pass less and more data using lambdas.We've created custom signals, and shown how to intercept events, pass onevent handling and use .accept()
and .ignore()
to hide/show eventsto the UI-parent widget. In the next section we will go on to takea look at two common features of the GUI — toolbars and menus.