Qt Signal Slot Pass Reference

Qt Signal Slot Pass Reference Rating: 9,5/10 8648 reviews

The Meta-Object system

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. 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.

Qt's meta-object system provides the signals and slots mechanism for inter-object communication, run-time type information (RTTI), and the dynamic property system. Signals and slots is one of the most important concepts in Qt, and it will be discussed in the next chapter.

The meta-object system is implemented with a three-part mechanism:

  • QObject is the base class that all objects in the meta-object system inherit from.
  • The Q_OBJECT macro is used to enable meta-object features when declared within a class definition.
  • The Meta-Object Compiler (moc) will read the class definitions with the declared Q_OBJECT macro and produce the meta-object code.

Value Type and Identity Type

Value types can be copied and assigned. Many of the Qt value types, such as QString and Qt Containers, also use implicit sharing (copy-on-write). Implicitly shared classes are both safe and efficient when passed as arguments, because only a pointer to the data is passed around, and the data is copied only if and when a function writes to it. Custom value types can be made known to the meta-object system by using the Q_DECLARE_METATYPE macro, which makes them storable in QVariant. This is useful when, for example, reading properties. How this is done will be discussed later in the chapter, with the property system in general.

Identity type in turn derives from QObject. It extends C++ with many dynamic features using a meta-object system. QObject has neither a copy constructor nor an assignment operator; this is by design. Actually, they are declared, but in a private section with the macro Q_DISABLE_COPY(). In fact, all Qt classes derived from QObject (direct or indirect) use this macro to declare their copy constructor and assignment operator to be private. The main consequence is that you should use pointers to QObject (or to your QObject subclass) where you might otherwise be tempted to use your QObject subclass as a value. For example, without a copy constructor, you can't use a subclass of QObject as the value to be stored in one of the container classes. You must store pointers.

The Qt Object Model and the QObject class

The QObject class is the base class of all Qt objects. It is the heart of the Qt Object Model. The central feature in this model is a very powerful mechanism for seamless object communication called signals and slots. The signals and slots system will be discussed at length in the next chapter.

QObjects organize themselves in object trees. When you create a QObject with another object as parent, the object will automatically add itself to the parent's children() list. The parent takes ownership of the object; i.e., it will automatically delete its children in its destructor. You can look for an object by name and optionally type using findChild() or findChildren(). The parent-child relationship will be discussed in chapter 2.03.

Every object has an objectName() and its class name can be found via the corresponding metaObject() (see QMetaObject::className()). You can determine whether the object's class inherits another class in the QObject inheritance hierarchy by using the inherits() function.

When an object is deleted, it emits a destroyed() signal. You can catch this signal to avoid dangling references to QObjects.

Last but not least, QObject provides the basic timer support in Qt; see QTimer for high-level support for timers.

The Q_OBJECT macro

The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt's meta-object system.

The moc tool reads a C++ header file. If it finds one or more class declarations that contain the Q_OBJECT macro, it produces a C++ source file containing the meta-object code for those classes. This meta-object code implements the underlying functionality needed for the runtime features. When using qmake you don't have to manually run the moc tool, it will be done automatically.

Example:

This macro requires the class to be a subclass of QObject. You can use Q_GADGET instead of Q_OBJECT to enable the meta object system's support for enums in a class that is not a QObject subclass. The Q_GADGET macro is a lighter version of the Q_OBJECT macro for classes that do not inherit from QObject but still want to use some of the reflection capabilities offered by QMetaObject. Just like the Q_OBJECT macro, it must appear in the private section of a class definition.

Notice that the Q_OBJECT macro is mandatory for any object that implements signals, slots or properties. We strongly recommend the use of this macro in all subclasses of QObject regardless of whether or not they actually use signals, slots and properties, since failure to do so may lead certain functions to exhibit strange behavior.

Internationalization (I18n)

All QObject subclasses support Qt's translation features, making it possible to translate an application's user interface into different languages.

To make user-visible text translatable, it must be wrapped in calls to the tr() function. This is explained in detail in the Writing Source Code for Translation document. We won't be covering internationalization on this course, but it's good to know it's there.

The Property System

Qt provides a sophisticated property system similar to the ones supplied by some compiler vendors. However, as a compiler- and platform-independent library, Qt does not rely on non-standard compiler features like __property or [property]. The Qt solution works with any standard C++ compiler on every platform Qt supports. It is based on the Meta-Object System that also provides the signals and slots system.

Requirements for Declaring Properties

To declare a property, use the Q_PROPERTY() macro in a class that inherits QObject. Here is an example showing how to export member variables as Qt properties using the MEMBER keyword. Note that a NOTIFY signal must be specified to allow QML property bindings. We will talk more about QML in Part 3!

A property behaves like a class data member, but it has additional features accessible through the Meta-Object System

The property declaration syntax has multiple keywords to specify the behaviour of the declared property. Here are the most relevant ones:

  • READ - For reading the property value. Ideally a const function is used for this purpose, and it must return either the property's type or a const reference to that type. eg., QWidget::focus is a read-only property with READ function, QWidget::hasFocus(). Required if no MEMBER variable was specified.
  • WRITE - For setting the property value. It must return void and must take exactly one argument, either of the property's type or a pointer or reference to that type. e.g., QWidget::enabled has the WRITE function QWidget::setEnabled(). Read-only properties do not need WRITE functions. e.g., QWidget::focus has no WRITE function.
  • MEMBER - Required if no READ accessor function is specified. This makes the given member variable readable and writable without the need of creating READ and WRITE accessor functions. It's still possible to use READ or WRITE accessor functions in addition to MEMBER variable association (but not both), if you need to control the variable access.
  • RESET - Optional. For setting the property back to its context specific default value. e.g., QWidget::cursor has the typical READ and WRITE functions, QWidget::cursor() and QWidget::setCursor(), and it also has a RESET function, QWidget::unsetCursor(), since no call to QWidget::setCursor() can mean reset to the context specific cursor. The RESET function must return void and take no parameters.
  • NOTIFY - Optional. If defined, it should specify one existing signal in that class that is emitted whenever the value of the property changes. NOTIFY signals for MEMBER variables must take zero or one parameter, which must be of the same type as the property. The parameter will take the new value of the property. The NOTIFY signal should only be emitted when the property has really been changed, to avoid bindings being unnecessarily re-evaluated in QML, for example. Qt emits automatically that signal when needed for MEMBER properties that do not have an explicit setter.
  • USER - Attribute indicates whether the property is designated as the user-facing or user-editable property for the class. Normally, there is only one USER property per class (default false). e.g., QAbstractButton::checked is the user editable property for (checkable) buttons.

The property type can be any type supported by QVariant, or it can be a user-defined type. In this example, class QDate is considered to be a user-defined type.

Because QDate is user-defined, you must include the <QDate> header file with the property declaration.

For historical reasons, QMap and QList as property types are synonym of QVariantMap and QVariantList.

Reading and Writing Properties

A property can be read and written using the generic functions QObject::property() and QObject::setProperty(), without knowing anything about the owning class except the property's name. In the code snippet below, the call to QAbstractButton::setDown() and the call to QObject::setProperty() both set property 'down'.

Accessing a property through its WRITE accessor is the better of the two, because it is faster and gives better diagnostics at compile time, but setting the property this way requires that you know about the class at compile time. Accessing properties by name lets you access classes you don't know about at compile time. You can discover a class's properties at run time by querying its QObject, QMetaObject, and QMetaProperties.

In the above snippet, QMetaObject::property() is used to get metadata about each property defined in some unknown class. The property name is fetched from the metadata and passed to QObject::property() to get the value of the property in the current object.

Example

Suppose we have a class MyClass, which is derived from QObject and which uses the Q_OBJECT macro in its private section. We want to declare a property in MyClass to keep track of a priority value. The name of the property will be priority, and its type will be an enumeration type named Priority, which is defined in MyClass.

We declare the property with the Q_PROPERTY() macro in the private section of the class. The required READ function is named priority, and we include a WRITE function named setPriority.

The enumeration type must be registered with the Meta-Object System using the Q_ENUM() macro. The macro registers an enum type with the meta-object system. This will enable useful features; for example, if used in a QVariant, you can convert them to strings. Likewise, passing them to QDebug will print out their names. It must be placed after the enum declaration in a class that has the Q_OBJECT or the Q_GADGET macro.

Registering an enumeration type makes the enumerator names available for use in calls to QObject::setProperty(). We must also provide our own declarations for the READ and WRITE functions.

Given a pointer to an instance of MyClass or a pointer to a QObject that is an instance of MyClass, we have two ways to set its priority property:

In the example, the enumeration type that is the property type is declared in MyClass and registered with the Meta-Object System using the Q_ENUM() macro. This makes the enumeration values available as strings for use as in the call to setProperty(). Had the enumeration type been declared in another class, its fully qualified name (i.e., OtherClass::Priority) would be required, and that other class would also have to inherit QObject and register the enumeration type there using the Q_ENUM() macro.

A similar macro, Q_FLAG(), is also available. Like Q_ENUM(), it registers an enumeration type, but it marks the type as being a set of flags, i.e. values that can be OR'd together. An I/O class might have enumeration values Read and Write and then QObject::setProperty() could accept Read Write. Q_FLAG() should be used to register this enumeration type.

Dynamic Properties

QObject::setProperty() can also be used to add new properties to an instance of a class at runtime. When it is called with a name and a value, if a property with the given name exists in the QObject, and if the given value is compatible with the property's type, the value is stored in the property, and true is returned. If the value is not compatible with the property's type, the property is not changed, and false is returned. But if the property with the given name doesn't exist in the QObject (i.e., if it wasn't declared with Q_PROPERTY()), a new property with the given name and value is automatically added to the QObject, but false is still returned. This means that a return of false can't be used to determine whether a particular property was actually set, unless you know in advance that the property already exists in the QObject.

Note that dynamic properties are added on a per instance basis, i.e., they are added to QObject, not QMetaObject. A property can be removed from an instance by passing the property name and an invalid QVariant value to QObject::setProperty(). The default constructor for QVariant constructs an invalid QVariant.

Dynamic properties can be queried with QObject::property(), just like properties declared at compile time with Q_PROPERTY().

Properties and Custom Types

Custom value types used by properties need to be registered using the Q_DECLARE_METATYPE() macro so that their values can be stored in QVariant objects. This makes them suitable for use with both static properties declared using the Q_PROPERTY() macro in class definitions and dynamic properties created at run-time.

The declaration is done by putting the Q_DECLARE_METATYPE() macro in the header file of your custom class. For example:

Adding Additional Information to a Class

Connected to the property system is an additional macro, Q_CLASSINFO(), that can be used to attach additional name--value pairs to a class's meta-object, for example:

Q_CLASSINFO('Version', '3.0.0')

Qt Signal Slot Pass Reference

Like other meta-data, class information is accessible at run-time through the meta-object; see QMetaObject::classInfo() for details.

In this exercise you'll get familiar with accessing QObject's properties. You'll find the instuctions in reflection.cpp.

Hint: QVariant will be useful.

This exercise is tad bit larger. You'll create a custom class Student, as well as implement the functionality for StudentRegistry. You'll find the instuctions in studentregistry.cpp.

Nearly all UI toolkits have a mechanism to detect a user action, and respond to this action. Some of them use callbacks, others use listeners, but basically, all of them are inspired by the observer pattern.

Observer pattern is used when an observable object wants to notify other observers objects about a state change. Here are some concrete examples:

  • A user has clicked on a button, and a menu should be displayed.
  • A web page just finished loading, and a process should extract some information from this loaded page.
  • An user is scrolling through a list of items (in an app store for example), and reached the end, so other items should be loaded.

Observer pattern is used everywhere in GUI applications, and often leads to some boilerplate code. Qt was created with the idea of removing this boilerplate code and providing a nice and clean syntax, and the signal and slots mechanism is the answer.

Signals and slots are the key of Qt and object communication within. They are in a sense comparable to callbacks, but the difference is that they are type-safe where as callbacks typically are not. One of the benefits we could mention before starting, is that signals and slots allow you to build many-to-many connections, where as typically virtual methods are one-to-one or one-to-many, if several virtual functions are used.

We discussed the Q_OBJECT macro in the last chapter, and it will find some relevance throughout this topic as well.

Briefly about Events

Before jumping into signals and slots, let's talk briefly about events. Events are executed in event loops. This is hardly specific to Qt, arguably most of the applications you use spend majority of their time in event loops which wait for input, let it be from user, network, or somewhere else. There can be multiple event loops, every thread will have one for example. Qt supports the use of Event Handlers but in general you'll want to use the signal and slot system. Reason we are introducing events here is that you should understand the concept of event loops as it relates to signals and slots. In the course we'll be dealing with single-threaded applications, but when you send signals across threads you should remember that the slot might not be executed immediately, and instead it might be placed in the receiving thread's event loop to wait until the control is given to that thread.

Performance

Compared to callbacks, signals and slots are slightly slower because of the increased flexibility they provide, but the difference for real applications is insignificant. In general, emitting a signal that is connected to some slots, is approximately ten times slower than calling the receivers directly, with non-virtual function calls. This is the overhead required to locate the connection object, to safely iterate over all connections (i.e. checking that subsequent receivers have not been destroyed during the emission), and to marshall any parameters in a generic fashion.

While ten non-virtual function calls may sound like a lot, it's much less overhead than any new or delete operation, for example. As soon as you perform a string, vector or list operation that behind the scene requires new or delete, the signals and slots overhead is only responsible for a very small proportion of the complete function call costs. The same is true whenever you do a system call in a slot; or indirectly call more than ten functions. The simplicity and flexibility of the signals and slots mechanism is well worth the overhead, which your users won't even notice.

Signals

Signals are emitted by an object when its internal state has changed in some way that might be interesting to the object's client or owner. Signals are public access functions and can be emitted from anywhere, but we recommend to only emit them from the class that defines the signal and its subclasses.

To define a signal, put it in the signals: block in the class definition:

To emit a signal, you use the emit keyword. The keyword is purely syntactic, but it helps to differentiate it from normal function calls.

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.

Here are some examples of signals from the QPushButton class:

  • clicked
  • pressed
  • released
Qt signal slot pass reference sheet

As you can see, their names are quite explicit. These signals are sent when the user clicked (pressed then released), pressed or released the button.

These signals are automatically generated by the moc (meta-object compiler) and must not be implemented in the .cpp file. They can never have return types (i.e. use void).

Developer experience shows that signals and slots are more reusable if they do not use special types. If QScrollBar::valueChanged() were to use a special type such as the hypothetical QScrollBar::Range, it could only be connected to slots designed specifically for QScrollBar. Connecting different input widgets together would be impossible.

Slots

A slot is called when a signal connected to it is emitted. Slots are normal C++ functions and can be called normally; their only special feature is that signals can be connected to them.

Here are some slots, from different classes:

  • QApplication::quit
  • QWidget::setEnabled
  • QPushButton::setText

If several slots are connected to one signal, the slots will be executed one after the other, in the order they have been connected, when the signal is emitted.

Since slots are normal member functions, they follow the normal C++ rules when called directly. However, as slots, they can be invoked by any component, regardless of its access level, via a signal-slot connection. This means that a signal emitted from an instance of an arbitrary class can cause a private slot to be invoked in an instance of an unrelated class.

Signal/Slot definition

As mentioned in the previous chapter, all classes that use the signal/slot system need to have the Q_OBJECT macro in the private section of the classes definition. Here's an example of a header file for a class that implements both signals and slots.

Connecting Signals and Slots

To connect the signal to the slot, we use QObject::connect(). There are several ways to connect signal and slots. The first is to use function pointers:

There are several advantages to using QObject::connect() with function pointers. First, it allows the compiler to check that the signal's arguments are compatible with the slot's arguments. Arguments can also be implicitly converted by the compiler, if needed.

You can also connect to functors or C++11 lambdas:

The traditional way to connect a signal to a slot is to use QObject::connect() and the SIGNAL() and SLOT() macros. It's presented here because it's still widely used, but in general, you should use one of the newer connection types presented before. The rule about whether to include arguments or not in the SIGNAL() and SLOT() macros, if the arguments have default values, is that the signature passed to the SIGNAL() macro must not have fewer arguments than the signature passed to the SLOT() macro.

All of these would work:

But this one won't work:

...because the slot will be expecting a QObject that the signal will not send. This connection will report a runtime error. Note that signal and slot arguments are not checked by the compiler when using this QObject::connect() overload.

In this exercise you'll create two classes to practise defining signals and slots. You'll find the exercise instructions in main.cpp.

3rd Party libraries

You might be familiar with other signals slot mechanisms, like the Boost.Signals2 library It is possible to use Qt with a 3rd party signal/slot mechanism or even use both mechanisms in the same project. Add the following definition to your projects (.pro) file.

It tells Qt not to define the moc keywords signals, slots, and emit, because these names will be used by a 3rd party library, e.g. Boost. Then to continue using Qt signals and slots with the no_keywords flag, simply replace all uses of the Qt moc keywords in your sources with the corresponding Qt macros Q_SIGNALS (or Q_SIGNAL), Q_SLOTS (or Q_SLOT), and Q_EMIT.

Object Communication with Events

While it is typically preferred to use signals and slots in object communication, there are cases, where the needed functionality is easier to handle with events. If we liked to emit several signals, for example, we can subclass the corresponding QObject and re-implement the event handler. The event handler can emit the signals without connecting a signal to a slot, which then emits several signals.

Qt is an event-based system. The GUI thread enters the event loop, when QCoreApplication::exec() is called. QCoreApplication can handle each event in the GUI thread and forward events to QObjects. The receiving QObject may handle or just ignore the corresponding event.

Events may be spontaneous or synthetic. Spontaneous events are created outside the application process, e.g. by the window manager, and sent to the application. In case of GUI events, a Platform Abstraction Plugin (QPA) receives the events and converts those to Qt event types. Qt events are value types, derived from QEvent, which provides a type enumeration for each event. If we like to modify the timer event handling, it could be done by re-implementing QObject::event() function as shown below.

First, the event type will be checked. If the event type is a QEvent::Timer, we downcast QEvent to QTimerEvent to access the event-specific data. In case of a timer, it is a timerId. The function returns a boolean value to tell the event system, if the event is propagated further to a next receiving object, if one exists. All events, which are not handled in this function, are handled by the base class.

Qt Signal Slot Pass Reference Sheet

Normally, there is no need to re-implement the event() function, but some event-specific handler function (See: Event Handlers). For example, timer events can be handled in void CustomObject::timerEvent(QTimerEvent *event). Note that these functions do not return a boolean value, but they either accept or ignore the event with QEvent::accept() and QEvent::ignore() to tell the event system, if the event should be propagated further or not.

Event Filters

If there is a need to handle the same event in several different classes in the same way, it is easier to use event filters rather than sub-classing many types. Event filter is nothing more but a QObject member function, which may be called before the actual event handling functions QObject::eventFilter(QObject *watched, QEvent *event). Similarly to the QObject::event() function, the boolean return value tells, if the event is filtered out (true) or whether it should be propagated further (false). Only installed event filters will be actually called void QObject::installEventFilter(QObject *filterObject).

There are two kinds of event filters: application-wide and object local event filters. The only difference is to which object the event filter is installed. If it is installed to QCoreApplication object, all events in the main thread will go through the event filter. If it is installed to some other QObject subclass, only events sent to that object will go through the event filter.

Application-wide event filters are useful for debugging to check, if for example the window manager gives the expected events to the Qt application. In general, avoid application wide event filters, as calling an extra functions for each event in the application may affect the event handling performance.

In most cases, it is sufficient to handle the event in an event handler, but as we have seen, it is possible to capture the event earlier in the propagation. For example, for touch events, there is no touch-specific event handlers, so they must be handled in QObject::event() function. Event filters are useful, if you want to have similar kind of event handling for several different types.

Custom Events

Sometimes there is no Qt event type, which is suitable for notifying about a specific action. In those cases, it is possible to create custom events. Those can be easily drived from QEvent. Each (custom) event contains event-specific data, so you need to add a member data and implement accessor functions to get and set the data. Finally, the event must be recognized by Qt event system, so you need a unique event type for you event. You may just extend the existing event enumeration, as shown in the example below.

Synchronous and asynchronous events

In Qt, events can be sent either synchronously and asynchronously. Asynchronous events are queued in the event queue QCoreApplication::postEvent(), which is managed by one of the platform-specific sub-classes of QAbstractEventDispatcher. Synchronous events are never queued QCoreApplication::sendEvent(). Note also that asynchronous events are managed by the event system, which means that they must be allocated in the heap and never deleted by the developer code.

Asynchronous events are thread-safe. In fact, cross-thread signals and slots are based on asynchronous event. When a signal is emitted from an object in one thread to an object in another thread, there will be actually an event sent between the threads, assuming that the connection type is either automatic or queued. When the event is handled in another thread, the handler code calls the slot automatically.

As any thread in Qt can have its own event loop, it is important not to interrupt the event handling by calling slots or any functions directly from another thread. It is safe to use queued connections or asynchronous events. The event is queued as long as the receiving thread returns to the event loop and starts handling the new event. Also, when you for example notify from a worker thread to the GUI thread that new data is available, you should always do this asynchronously.

Implement CustomObject by subclassing QObject, and:

  • Start a timer in the constructor (e.g. 3 sec).
  • Re-implement the event() function. Check if there's a CustomEvent and print to the debug console: 'Custom event handled: Event data ''
  • Re-implement the timerEvent() function. When your timer expires, quit the application.

Implement a CustomEvent class with a string member.

With the code provided in main.cpp the program should print two messages to the debug console and then exit the application after the timer runs out.

Further Reading

For more detailed information about Signals and Slots, see the official Signal/Slot documentation.

In an event-based system, it is important to keep the GUI thread as responsive as possible and do all time-consuming tasks in worker threads. Time-consuming tasks may take just a few dozens of milliseconds or they may execute an infinite loop. In any case, blocking functions should not delay event handling in the GUI thread.

QThread

QThread is a class to manage threads in a platform-independent way. The threads themselves are platform-specific kernel objects. QThread provides an API to set the priority with a platform-independent enumeration - setPriority(QThread::Priority), start the thread - start(QThread::Priority), and exit from the thread event loop - exit(int returnCode). The start() function results that QThread::run() will be executed in a new thread after the new thread is scheduled. The run() function starts a thread-specific event loop by calling QThread::exec(). The signals started() amd finished() notify, when the thread is going to be started, i.e. the run() function is not yet started and after the thread has finished executing the event loop, respectively.

Note that there is also a terminate() function to kill the thread. The use of this function is not encouraged, as it terminates the thread immediately possibly in the middle of data handling or holding a locked mutex object. This may break the data integrity or result to deadlocks. The best practise is always nicely return from the event loop and then thread run() function.

If there is no need to control thread execution, it is much easier to use QRunnable objects. Using runnables requires a re-implementation of the run() function, but runnables will be executed in threads, provided by the thread pool and there is no need to create and cleanup threads manually.

Thread Affinity

Thread affinity defines to which thread a QObject instance belongs to. This information is needed to decide, whether a signal between two objects should be queued or not, if automatic connection type is used. In practise, the thread affinity is just QThread *QObject::thread() return value. If the value is 0, the thread cannot receive signals or posted events.

Developers should pay extra attention to have a correct thread affinity in their Qt programs. Although the concept is rather straightforward, it’s easy to create nasty errors, which seem to happen randomly and are challenging to debug and test. For example, in the class declaration below, it is easily possible to have wrong thread affinity for the timer member. MyThread is instantiated in the calling thread, which means that its QObject members will be instantiated in the calling thread as well. If we try to start or stop the timer in the run() function, there will be a run-time error: “Timers cannot be started from another thread”.

Pass

To fix the problem, you need to change the timer thread affinity before it will be used in the run() function. QObject::moveToThread(QThread *) changes the object’s thread affinity. The effect is the same, as the timer had been created in the run() function. Pay attention to the base call in the run() function to start the thread event loop.

Usually, there is no need to subclass QThread at all. The recommendation is that you create a worker QObject and then change the thread affinity of that object. Typically, this results to less error-prone code.

Background Tasks

Let’s see, how to create background tasks with worker objects without subclassing QThread. A trivial worker object should have a function, which is executed in the thread. In the following example, it is the run() function. The worker is finished after the timer expires, which results that the thread gets a notification that it can quit the event loop. Note that the timer is a pointer member and in the worker constructor, the timer parent is set to the worker itself. This is convenient, as when we change the worker thread affinity, all its children thread affinity will change as well. The parent and its children cannot have different thread affinity.

Signal

The code needed to start the worker becomes quite trivial boilerplate code. We create the worker and thread objects, and change the worker thread affinity to the new thread. We must not start the worker before the thread is running. Otherwise the worker is executed by the main thread. After the worker is finished it notifies the main thread and the new thread to quit from the event loops. After the thread has finished the event loop, we cleanup the heap.

Graceful Death

If the worker object is running a busy loop, how we should nicely finish the thread. Remember that we should avoid using QThread::terminate(). QThread provides nice functions to request thread - interruption requestInterruption() and to check, whether the interruption has been requested - isInterruptionrequested(). These functions can be even used in threads, which are not running an event loop. When the timer expires, our worker stops the timer and notifies the thread to be stopped. Any event or signal can be used to interrupt the thread.

Qt Signal Slot Pass Reference Generator

Our worker object is running a busy loop, as long as the timer expires. While in the busy loop, the thread does not handle any events, so we need to check occasionally, if there are any events to be handled. The event dispatcher function processEvents() allows us to handle any pending events. Without this function, our busy loop would run forever.

We have used signals and slots heavily to communicate between the threads. They are thread safe and make the inter-thread communication between threads rather straightforward. In addition to signals and slots, you may use QMetaObbject::invokeMethod(). This is useful to notify, for example state changes from a worker thread to a GUI thread. Never use direct connections or direct function calls, when notifying the GUI thread from worker threads.

Thread Synchronization

Qt provides several lock types for mutual exclusion and thread synchronization. QMutex provides a recursive mutual exclusion lock. There are also QReadLocker and QReadWriteLocker to optimize locking in cases, where most shared data accesses will be read only. QSemaphore provides a counting semaphore between threads in a single process and QSystemSemaphore between threads in multiple processes. QWaitCondition can be used to synchronize threads, waiting for a condition to become true. Please read further information from here.

Implement a WorkerObject, which is executed in a separate thread from the GUI thread.

  • The worker object should calculate Fibonacci numbers up to a requested number (e.g. if 5 numbers are requested, you should get 0,1,1,2,3)
  • The worker object should have a timer to interrupt calculations.
  • The worker object thread should call the slot in FibonacciApplication to show fibonacci values from 0 to the requested number.
  • The worker finishes, if all Fibonacci values up to the requested number have been calculated OR the timer expires. In both cases you should nicely exit from the application.

Create a FibonacciApplication by subclassing QCoreApplication and add a slot, which prints out a number into the debug console.

The Object Tree

QObjects organize themselves in object trees. When you create a QObject with another object as parent, it's added to the parent's children() list, and is deleted when the parent is. It turns out that this approach fits the needs of GUI objects very well. For example, a QShortcut (keyboard shortcut) is a child of the relevant window, so when the user closes that window, the shortcut is deleted too.

QQuickItem, the basic visual element of the Qt Quick module which we'll discuss in later parts of the course, inherits from QObject, but has a concept of the visual parent which differs from that of the QObject parent. An item's visual parent may not necessarily be the same as its object parent. See Concepts - Visual Parent in Qt Quick for more details.

You can also delete child objects yourself, and they will remove themselves from their parents. For example, when the user removes a toolbar it may lead to the application deleting one of its QToolBar objects, in which case the toolbar's QMainWindow parent would detect the change and reconfigure its screen space accordingly.

Object and pointer permanence

When QObjects are created on the heap (i.e., created with new), a tree can be constructed from them in any order, and later, the objects in the tree can be destroyed in any order. When any QObject in the tree is deleted, if the object has a parent, the destructor automatically removes the object from its parent. If the object has children, the destructor automatically deletes each child. No QObject is deleted twice, regardless of the order of destruction.

When QObjects are created on the stack, the same behavior applies. Normally, the order of destruction still doesn't present a problem. Consider the following snippet:

The parent (window), and the child (quit) are both QObjects because QPushButton inherits QWidget, and QWidget inherits QObject. This code is correct: the destructor of quit is not called twice because the C++ language standard (ISO/IEC 14882:2003) specifies that destructors of local objects are called in the reverse order of their constructors. Therefore, the destructor of the child, quit, is called first, and it removes itself from its parent, window, before the destructor of window is called.

But now consider what happens if we swap the order of construction, as shown in this second snippet:

In this case, the order of destruction causes a problem. The parent's destructor is called first because it was created last. It then calls the destructor of its child, quit, which is incorrect because quit is a local variable. When quit subsequently goes out of scope, its destructor is called again, this time correctly, but the damage has already been done.

To summarize:

  • Tree can be constructed in any order
  • Tree can be destroyed in any order
  • if object has parent: object first removed from parent
  • if object has children: deletes each child first
  • No object is deleted twice

Please note that Parent-child relationship is NOT the same things as inheritance.

The Dangling Pointer Problem

The object tree does not solve the dangling pointer problem, but QPointer provides a guarded pointer for QObject. When the referenced object is destroyed, it sets the pointer to 0. It's be easy to mix guarded and normal pointers. The guarded pointer is automatically cast to the pointer type.

Qt objects may also notify observers just before their destruction.

This exercise will be mostly for the student to play around with the concept of parents and children. It is not tested in TMC, and doesn't award points. You'll find instructions in parenting.cpp.

Exercise for Part 2 - Directory Browser

This exercise is the first that you'll do outside of TMC. Download the template from the above link, and complete it following the exercise instructions.

After you're done, create a repository for your solution on GitHub, and submit it to be peer-reviewed below. Please note that to get the points for this exercise, you're required to review another student's solution! You can download an example solution and do the peer-review after you've submitted your own solution.

Complete the implementation of a trivial directory browser. You are provided with a simple browser QML UI and your task is to complete the implementation. The UI shows a directory name and number of file entries and a list of all entries. If the grid at the top is clicked, the application should sort the files to descending or ascending order, depending on the current order. Clicking on the file will show the files in that folder or open a text editor, if the file is a text file.

  • Create a new class DirManager by deriving QObject to implement the requested functionality.
  • Instantiate and expose your object to the root context of the QML engine in main.cpp line 11.
  • When a program is started, read at least file names and sizes of a directory, e.g. home, into a container. Note that the container must be a string list, so concatenate the name and size to a single string.

The QML UI expects the following API from your QObject sub-class.

  • There must be two properties: dirName of type String and filesInDir of type int. The values should correspond the real values of the current directory.
  • Provide the slot functions to be called from the UI.
  • model() returns the container.
  • fileContent() returns the content of a text file as String.
  • sort() will sort entries in ascending or descending order.
  • entryChanged(QString) is called from the UI. You should check, whether the entry is a directory or a file. In the former case, you should read new entries from the new directory and in the latter case, you should read content of the file and use fileContent() to return the content to the UI.

The UI is heavily based on signals.

  • dirNameChanged() indicates the directory name has changed, i.e. the user has clicked on the directory name on the UI.
  • Similarly, filesInDirChanged() indicates that the filesInDir value has changed.
  • fileContentChanged() is similar, indicating the user has clicked on the file name in the UI and new data has been read from the file.
  • dataChanged() notifies the container content has changed.
  • entryClicked(QString) signal is emitted from the UI. You should handle this by checking the type of the entry and by reading either a directory content to the container or file content to the String, used in the UI.

This is the sequel of my previous article explaining the implementation details of the signals and slots.In the Part 1, we have seenthe general principle and how it works with the old syntax.In this blog post, we will see the implementation details behind thenew function pointerbased syntax in Qt5.

New Syntax in Qt5

The new syntax looks like this:

Why the new syntax?

I already explained the advantages of the new syntax in adedicated blog entry.To summarize, the new syntax allows compile-time checking of the signals and slots. It also allowsautomatic conversion of the arguments if they do not have the same types.As a bonus, it enables the support for lambda expressions.

New overloads

There was only a few changes required to make that possible.
The main idea is to have new overloads to QObject::connect which take the pointersto functions as arguments instead of char*

There are three new static overloads of QObject::connect: (not actual code)

The first one is the one that is much closer to the old syntax: you connect a signal from the senderto a slot in a receiver object.The two other overloads are connecting a signal to a static function or a functor object withouta receiver.

They are very similar and we will only analyze the first one in this article.

Pointer to Member Functions

Before continuing my explanation, I would like to open a parenthesis totalk a bit about pointers to member functions.

Here is a simple sample code that declares a pointer to member function and calls it.

Pointers to member and pointers to member functions are usually part of the subset of C++ that is not much used and thus lesser known.
The good news is that you still do not really need to know much about them to use Qt and its new syntax. All you need to remember is to put the & before the name of the signal in your connect call. But you will not need to cope with the ::*, .* or ->* cryptic operators.

These cryptic operators allow you to declare a pointer to a member or access it.The type of such pointers includes the return type, the class which owns the member, the types of each argumentand the const-ness of the function.

You cannot really convert pointer to member functions to anything and in particular not tovoid* because they have a different sizeof.
If the function varies slightly in signature, you cannot convert from one to the other.For example, even converting from void (MyClass::*)(int) const tovoid (MyClass::*)(int) is not allowed.(You could do it with reinterpret_cast; but that would be an undefined behaviour if you callthem, according to the standard)

Pointer to member functions are not just like normal function pointers.A normal function pointer is just a normal pointer the address where thecode of that function lies.But pointer to member function need to store more information:member functions can be virtual and there is also an offset to apply to thehidden this in case of multiple inheritance.
sizeof of a pointer to a member function can evenvary depending of the class.This is why we need to take special care when manipulating them.

Type Traits: QtPrivate::FunctionPointer

Let me introduce you to the QtPrivate::FunctionPointer type trait.
A trait is basically a helper class that gives meta data about a given type.Another example of trait in Qt isQTypeInfo.

What we will need to know in order to implement the new syntax is information about a function pointer.

The template<typename T> struct FunctionPointer will give us informationabout T via its member.

  • ArgumentCount: An integer representing the number of arguments of the function.
  • Object: Exists only for pointer to member function. It is a typedef to the class of which the function is a member.
  • Arguments: Represents the list of argument. It is a typedef to a meta-programming list.
  • call(T &function, QObject *receiver, void **args): A static function that will call the function, applying the given parameters.

Qt still supports C++98 compiler which means we unfortunately cannot require support for variadic templates.Therefore we had to specialize our trait function for each number of arguments.We have four kinds of specializationd: normal function pointer, pointer to member function,pointer to const member function and functors.For each kind, we need to specialize for each number of arguments. We support up to six arguments.We also made a specialization using variadic templateso we support arbitrary number of arguments if the compiler supports variadic templates.

The implementation of FunctionPointer lies inqobjectdefs_impl.h.

QObject::connect

The implementation relies on a lot of template code. I am not going to explain all of it.

Here is the code of the first new overload fromqobject.h:

You notice in the function signature that sender and receiverare not just QObject* as the documentation points out. They are pointers totypename FunctionPointer::Object instead.This uses SFINAEto make this overload only enabled for pointers to member functionsbecause the Object only exists in FunctionPointer ifthe type is a pointer to member function.

We then start with a bunch ofQ_STATIC_ASSERT.They should generate sensible compilation error messages when the user made a mistake.If the user did something wrong, it is important that he/she sees an error hereand not in the soup of template code in the _impl.h files.We want to hide the underlying implementation from the user who should not needto care about it.
That means that if you ever you see a confusing error in the implementation details,it should be considered as a bug that should be reported.

We then allocate a QSlotObject that is going to be passed to connectImpl().The QSlotObject is a wrapper around the slot that will help calling it. It alsoknows the type of the signal arguments so it can do the proper type conversion.
We use List_Left to only pass the same number as argument as the slot, which allows connectinga signal with many arguments to a slot with less arguments.

QObject::connectImpl is the private internal functionthat will perform the connection.It is similar to the original syntax, the difference is that instead of storing amethod index in the QObjectPrivate::Connection structure,we store a pointer to the QSlotObjectBase.

The reason why we pass &slot as a void** is only tobe able to compare it if the type is Qt::UniqueConnection.

We also pass the &signal as a void**.It is a pointer to the member function pointer. (Yes, a pointer to the pointer)

Signal Index

We need to make a relationship between the signal pointer and the signal index.
We use MOC for that. Yes, that means this new syntaxis still using the MOC and that there are no plans to get rid of it :-).

MOC will generate code in qt_static_metacallthat compares the parameter and returns the right index.connectImpl will call the qt_static_metacall function with thepointer to the function pointer.

Once we have the signal index, we can proceed like in the other syntax.

The QSlotObjectBase

QSlotObjectBase is the object passed to connectImplthat represents the slot.

Before showing the real code, this is what QObject::QSlotObjectBasewas in Qt5 alpha:

It is basically an interface that is meant to be re-implemented bytemplate classes implementing the call and comparison of thefunction pointers.

It is re-implemented by one of the QSlotObject, QStaticSlotObject orQFunctorSlotObject template class.

Fake Virtual Table

The problem with that is that each instantiation of those object would need to create a virtual table which contains not only pointer to virtual functionsbut also lot of information we do not need such asRTTI.That would result in lot of superfluous data and relocation in the binaries.

In order to avoid that, QSlotObjectBase was changed not to be a C++ polymorphic class.Virtual functions are emulated by hand.

The m_impl is a (normal) function pointer which performsthe three operations that were previously virtual functions. The 're-implementations'set it to their own implementation in the constructor.

Please do not go in your code and replace all your virtual functions by such ahack because you read here it was good.This is only done in this case because almost every call to connectwould generate a new different type (since the QSlotObject has template parameterswich depend on signature of the signal and the slot).

Protected, Public, or Private Signals.

Signals were protected in Qt4 and before. It was a design choice as signals should be emittedby the object when its change its state. They should not be emitted fromoutside the object and calling a signal on another object is almost always a bad idea.

However, with the new syntax, you need to be able take the addressof the signal from the point you make the connection.The compiler would only let you do that if you have access to that signal.Writing &Counter::valueChanged would generate a compiler errorif the signal was not public.

In Qt 5 we had to change signals from protected to public.This is unfortunate since this mean anyone can emit the signals.We found no way around it. We tried a trick with the emit keyword. We tried returning a special value.But nothing worked.I believe that the advantages of the new syntax overcome the problem that signals are now public.

Sometimes it is even desirable to have the signal private. This is the case for example inQAbstractItemModel, where otherwise, developers tend to emit signalfrom the derived class which is not what the API wants.There used to be a pre-processor trick that made signals privatebut it broke the new connection syntax.
A new hack has been introduced.QPrivateSignal is a dummy (empty) struct declared private in the Q_OBJECTmacro. It can be used as the last parameter of the signal. Because it is private, only the objecthas the right to construct it for calling the signal.MOC will ignore the QPrivateSignal last argument while generating signature information.See qabstractitemmodel.h for an example.

More Template Code

The rest of the code is inqobjectdefs_impl.h andqobject_impl.h.It is mostly standard dull template code.

I will not go into much more details in this article,but I will just go over few items that are worth mentioning.

Meta-Programming List

As pointed out earlier, FunctionPointer::Arguments is a listof the arguments. The code needs to operate on that list:iterate over each element, take only a part of it or select a given item.

Qt Signal Slot Pass Reference Generator

That is why there isQtPrivate::List that can represent a list of types. Some helpers to operate on it areQtPrivate::List_Select andQtPrivate::List_Left, which give the N-th element in the list and a sub-list containingthe N first elements.

The implementation of List is different for compilers that support variadic templates and compilers that do not.

With variadic templates, it is atemplate<typename... T> struct List;. The list of arguments is just encapsulatedin the template parameters.
For example: the type of a list containing the arguments (int, QString, QObject*) would simply be:

Without variadic template, it is a LISP-style list: template<typename Head, typename Tail > struct List;where Tail can be either another List or void for the end of the list.
The same example as before would be:

ApplyReturnValue Trick

In the function FunctionPointer::call, the args[0] is meant to receive the return value of the slot.If the signal returns a value, it is a pointer to an object of the return type ofthe signal, else, it is 0.If the slot returns a value, we need to copy it in arg[0]. If it returns void, we do nothing.

The problem is that it is not syntaxically correct to use thereturn value of a function that returns void.Should I have duplicated the already huge amount of code duplication: once for the voidreturn type and the other for the non-void?No, thanks to the comma operator.

In C++ you can do something like that:

You could have replaced the comma by a semicolon and everything would have been fine.

Where it becomes interesting is when you call it with something that is not void:

There, the comma will actually call an operator that you even can overload.It is what we do inqobjectdefs_impl.h

ApplyReturnValue is just a wrapper around a void*. Then it can be usedin each helper. This is for example the case of a functor without arguments:

This code is inlined, so it will not cost anything at run-time.

Conclusion

This is it for this blog post. There is still a lot to talk about(I have not even mentioned QueuedConnection or thread safety yet), but I hope you found thisinterresting and that you learned here something that might help you as a programmer.

Update:The part 3 is available.

Comments are closed.