C Signal Slot

Posted onby admin
  • The signals library achieves clean API design, standard conformity, the best possible runtime performance defeating virtual function, while also securing recursion- and modification-safety, all with just below 400 lines of code. I finally got some time to clean up my library based on a previous research. Despite having implemented observer.
  • Every class can disconnect its slot or signal at any time when it is not interested in events anymore. If a class is destroyed, it automatically disconnects all of its signals and slots. If, in the above example, class Y is destroyed, it disconnects from Slot A in Class X and from Signal 1 in Class X and Z. Basic Features Creating a Slot.

This article is the most comprehensive description of signals and slots in QML compared to all previous articles on this site.

In this article, I will try to explain the following when working with Qt/QML + Qt/C++:

  • ways to declare signals and slots, also called methods in the C ++ class, which will be registered in the QML layer
  • ways to connect to signals of classes declared in C ++ as context
  • work with Q_PROPERTY, which also requires signals and slots
  • ways to connect signals and slots in QML
  • etc.

Qml Signal To C Slot This program demonstrates how QML and C can be connected through Qt signalsand slots. It does this through embedding C code as a context property in QMLrather than explicitly connecting signals and slots. When the program is started, the C part send a signal to QML, including aparameter. In signal-slot mechanism, the slot runs in slot’s thread even though the signal is emitted in another thread. So, signal-slot mechanism also provides inter-thread communication without a hassle.

Signals and slots from the C++ class

Let's create our first class that will work with signals and slots in QML. This is one of the very first examples that I have already shown, but I will repeat this example so that the article is as complete as possible.

In this example, I want to create an application that has one button and by pressing this button increases the counter that is inside the C++ class. This C++ class will be registered as a context property in the QML engine of our application.

App appearance will be next

AppCore.h

Declaring signals and slots in C ++ code will not differ much from the classical Qt/C++.

AppCore.cpp

As well as the implementation of the methods themselves.

main.cpp

main.qml

And now the most interesting. How to use an object loaded in a QML context and how to connect to its signals.

As you remember, we loaded the object into the context QML under the name appCore , we will use this object to access it. But to connect to the signal, we will need to use the QML type Connections .

Thus, you can access the object that was loaded into the context of the QML engine, call its slot, and process the signal from this object.

It is also not necessary to declare receiveFromQml() as a slot in this case. This method can also be declared as Q_INVOKABLE method.

Using Q_PROPERTY

The next option is to use the Q_PROPERTY macro. A classic property in Qt might look like this for our task

This property has the following components:

  • type of property, as well as its name: int counter , which are bound to the variable int m_counter inside the class, this is the logic of code generation in Qt
  • name of the method to read, matches the name of the property: counter
  • method name for setting the value: setCounter
  • signal that reports property changes: counterChanged
Sigusr1

You can also pass additional parameters to this macro, but this is beyond the scope of this article. And also the property can be read only, that is, without a setter.

Now look at the full code using Q_PROPERTY

AppCore.h

AppCore.cpp

main.qml

Here you will see that connecting the property and accessing it has become easier thanks to the declarative style of QML code. Of course, you cannot always use properties, sometimes you just need to use signals, slots, and Q_INVOKABLE methods. But for variables like counter, properties are likely to be much more convenient.

Connecting signals inside QML files

Now consider the option of connecting signals and slots (functions) inside QML files. There will no longer be any C ++ code.

Among other things, you can use and disable signals from slots

Connect a signal to a signal

Also in QML there is still the ability to connect a signal to a signal, as in Qt/C++. Look at the following artificial example.

In this case, the counter will continue to increase when the button is pressed. But the button press signal is not connected directly to the counter increase function, but is forwarded through the signal.

Using Variables in Signals

QML also has the ability to use variables in signals.

Conclusion

For the most part, this entire article fits into several points:

  • In C ++, to interact with the QML layer, you can use signals, slots, Q_INVOKABLE methods, as well as create properties using the Q_PROPERTY macro
  • In order to respond to signals from objects, you can use the QML type Connections
  • Q_PROPERTY obeys the declarative style of QML and, when a property is changed, it can automatically set new values, if the property has been added to any object in QML. In this case, the signal slot connections are set automatically.
  • In QML, you can connect and disconnect signal / slot connections using the following syntax:
    • object1.signal.connect (object2.slot)
    • object1.signal.disconnect (object2.slot)
  • Signals in QML can also be connected to other signals, as is done in Qt / C ++
  • Signals in QML may also have arguments

Functions can be managed with the aid of the connect() and disconnect() member functions provided by boost::signals2::signal. Because connect() returns an object of type boost::signals2::connection, associations can also be managed differently (see Example 67.10).

Example 67.10. Managing connections with boost::signals2::connection

The disconnect() member function of boost::signals2::signal requires a function pointer to be passed in. Avoid this by calling disconnect() on the boost::signals2::connection object.

To block a function for a short time without removing the association from the signal, boost::signals2::shared_connection_block can be used.

Example 67.11. Blocking connections with shared_connection_block

Example 67.11 executes the lambda function twice. The signal s is triggered three times, but the lambda function is not called the second time because an object of type boost::signals2::shared_connection_block was created to block the call. Once the object goes out of scope, the block is automatically removed. A block can also be removed explicitly by calling unblock(). Because it is called before the last trigger, the final call to the lambda function is executed again.

In addition to unblock(), boost::signals2::shared_connection_block provides the member functions block() and blocking(). The former is used to block a connection after a call to unblock(), while the latter makes it possible to check whether or not a connection is currently blocked.

Please note that boost::signals2::shared_connection_block carries the word shared for a reason: multiple objects of type boost::signals2::shared_connection_block can be initialized with the same connection.

Example 67.12. Blocking a connection multiple times

Example 67.12 accesses s twice, but the lambda function is only called the second time. The program writes Hello, world! to the standard output stream only once.

Because false is passed to the constructor as the second parameter, the first object of type boost::signals2::shared_connection_block does not block the connection to the signal s. Hence, calling blocking() on the object b1 returns false.

Nevertheless, the lambda function is not executed when s is first accessed because the access happens only after a second object of type boost::signals2::shared_connection_block has been instantiated. By not passing a second parameter to the constructor, the connection is blocked by the object. When s is accessed for the second time, the lambda function is executed because the block was automatically removed once b2 went out of scope.

Boost.Signals2 can release a connection once the object whose member function is associated with a signal is destroyed.

C Signal Sigusr1

Example 67.13 associates the member function of an object with a signal, with the help of std::bind(). The object is destroyed before the signal is triggered, which is a problem because, instead of passing an object of type world, only an address was passed to std::bind(). By the time s() is called, the object referenced no longer exists.

C signal sigusr1

It is possible to modify the program so that the connection is automatically released once the object is destroyed. Example 67.14 does this.

Example 67.14. Releasing associated member functions automatically

Now num_slots() returns 0. Example 67.14 does not try to call a member function on an object that doesn’t exist when the signal is triggered. The change was to tie the object of type world to a smart pointer of type boost::shared_ptr, which is passed to track(). This member function is called on the slot that was passed to connect() to request tracking on the corresponding object.

A function or member function associated with a signal is called a slot. The type to specify a slot was not used in the previous examples because passing a pointer to a function or member function to connect() was sufficient. The corresponding slot was created and associated with the signal automatically.

In Example 67.14, however, the smart pointer is associated with the slot by calling track(). Because the type of the slot depends on the signal, boost::signals2::signal provides a type slot_type to access the required type. slot_type behaves just like std::bind, making it possible to pass both parameters to describe the slot directly. track() can then be called to associate the slot with a smart pointer of type boost::shared_ptr. The object is then tracked, which causes the slot to be automatically removed once the tracked object is destroyed.

To manage objects with different smart pointers, slots provide a member function called track_foreign(). While track() expects a smart pointer of type boost::shared_ptr, track_foreign() allows you to, for example, use a smart pointer of type std::shared_ptr. Smart pointers other than std::shared_ptr and std::weak_ptr must be introduced to Boost.Signals2 before they can be passed to track_foreign().

The consumer of a particular event can access an object of type boost::signals2::signal to create new associations or release existing ones.

Example 67.15. Creating new connections in an event handler

C++ Signal Slot

Example 67.15 accesses s inside the connect() function to associate a lambda function with the signal. Since connect() is called when the signal is triggered, the question is whether the lambda function will also be called.

The program does not output anything, which means the lambda function is never called. While Boost.Signals2 supports associating functions to signals when a signal is triggered, the new associations will only be used when the signal is triggered again.

Example 67.16. Releasing connections in an event handler

Example 67.16 does not create a new association, instead it releases an existing one. As in Example 67.15, this example writes nothing to the standard output stream.

This behavior can be explained quite simply. Imagine that a temporary copy of all slots is created whenever a signal is triggered. Newly created associations are not added to the temporary copy and therefore can only be called the next time the signal is triggered. Released associations, on the other hand, are still part of the temporary copy, but will be checked by the combiner when de-referenced to avoid calling a member function on an object that has already been destroyed.