Arguments automatic type conversion. Not only you can now use typedef or namespaces properly, but you can also connect signals to slots that take arguments of different types if an implicit conversion is possible. In the following example, we connect a signal that has a QString as a parameter to a slot that takes a QVariant. Basically, I was curious whether the signal/slot mechanism had some built-in protection against a race condition that this code would cause. I think you answered that: if within a thread, it's OK; if not, it's unsafe. Signals and slots are loosely coupled: A class which emits a signal neither knows nor cares which slots receive the signal. 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.
- Signals, slots, QOBJECT, emit, SIGNAL, SLOT. Those are known as the Qt extension to C. They are in fact simple macros, defined in qobjectdefs.h. #define signals public #define slots /. nothing./ That is right, signals and slots are simple functions: the compiler will handle them them like any other functions.
- Defining this macro will disable narrowing and floating-point-to-integral conversions between the arguments carried by a signal and the arguments accepted by a slot, when the signal and the slot are connected using the PMF-based syntax. This function was introduced in Qt 5.8. See also QObject::connect. QCLASSINFO (Name, Value).
Envision you've decided to go solo and start an indie game studio to fulfil your lifelong dream - to design and create a turn-based strategy game. You begin your endeavour by outlining the different components and sketch on the overall architecture of the game. The game will have some units which can be moved and controlled by using the mouse. There will be an AI component which will perhaps kick in after the units have completed their movements. The AI will then decide how the enemies should respond.
In other words, a sequence of actions need to happen: the mouse click, then the movement of units and then the response of the enemies. However, you probably don't want the mouse logic to directly depend on the unit-classes; it will be used for much more. For the same reason the unit-classes shouldn't need to rely on the AI nor the enemy logic; possibly the same AI will be used for many different enemies. I'm sure you're a great developer so you're aiming to decouple these different components. Nonetheless, the components need to communicate with each other and you need some kind of callback-mechanism. And this, ladies and gentlemen, this is where Qt's signals and slots comes to the rescue.
This is the third post in the series 'Crash course in Qt for C++ developers' covering the signals and slots mechanism. The other topics are listed below.
- Signals and slots - communication between objects
- Qt Quick/QML example
- Qt Widgets example
- Tooling, e.g. Qt Creator
- Remaining good-to-know topics
- Where to go from here?
The most common usage of the signals and slots mechanism is for inter-object communication and has been around since the birth of Qt. It's a system where an object broadcasts a signal with data and any listeners, including the sender itself, can subscribe to that signal. The listeners then redirect the data to a member function, or what's so called the slot, where it's then processed.
You might wonder how this system is different from using a standard callback-mechanism used in many other GUI tools. One benefit of Qt's solution is that the sender is not dependent on the listener, it's just firing off the signal. In a callback mechanism the sender usually needs to know which listeners to notify. Another benefit, in contrast to many other callback implementations, is that the signals and slots functions are type safe.
OK - enough text, let's look at some code.
California's Best Bet. No matching events listed under Entertainment. Please try viewing the full calendar for a complete list of events. Feather falls casino events. California's Best Bet. This site uses cookies. By continuing to browse the site, you are agreeing to our use of cookies. Feather Falls Casino tickets and upcoming 2020 event schedule. Find details for Feather Falls Casino in Oroville, CA, including venue info and seating charts. Valued Feather Falls Guests- All entertainment has been cancelled until further notice.
The basics
Signals
Let's start with signals. In order to enable signals for a class, it has to inherit from
QObject
and use the Q_OBJECT
macro. We'll define a signal called signalName
with three arguments. The arguments are used to pass in data, which will later be available in the slots.The
signals
keyword is essentially a simple macro defined to public
but is also used by the MOC (remember from the previous post?) to generate the implementation of the member function signalName()
. The implementation can be found in the generated file moc_mysender.cpp. The signal is declared similarly to a regular function, except for a constraint on the return value - it has to be void
. To emit the signal, we'll only call the function with the addition of appending emit
, i.e.:emit
is a macro defined to do.. nothing. It's only used for documentation and readability purposes. You could omit it: the code would compile fine and the outcome would be the same, but again, we want to be explicit that it's a signal. Also, notice that we don't need to depend on any of the listeners, we're only so far just emitting a signal. So how would an object listen to it?Slots
A listener object will redirect the signal to one of its slots. However, slots are not limited to member functions but can also consist of lambdas, non-member functions and functors. The arguably most common use is to define the slot within an object, so let's start by creating a new class
MyReceiver
which has a slot called slotName()
:Similarly to the signal object, the receiver class needs to inherit from
QObject
and also use the Q_OBJECT
macro. Furthermore, slots
is another macro declared to do nothing, but in contrast to emit
it's used by the MOC to generate introspection code. Slots-function can be specified as public, protected and private; Signals are always public.Did I mention that the system is type safe? The signal's and slot's signature must find a match in order to link them: the arguments must be of the same types and qualifiers and declared in the same order. So how do we connect our signal to our slot?
Connect
We'll use the static function QObject::connect():
QObject::connect()
takes five arguments, the last one has a default value and will be ignored for now. From left to right:- Start with the object which emits the signal - the sender.
- Next we have a pointer to a member function - the signal.
- Hook it up to the listener - the receiver.
- Last, we have another pointer to a member function - the slot.
Now, if we emit out signal, the slot will be notified and we get the following output:
Important data that will be sent to all listeners 42 1.618033
You may connect many different signals to the same slot, or use the same signal for many different slots. It's your choice - the system is very flexible. As mentioned, the system even allows to connect functions and lambdas directly, without using a listener, e.g:
The connection is automatically disconnected if either the sender or the receiver becomes deleted. However, as you might have guessed it's also possible to manually disconnect it by using disconnect:
Heads-up when capturing data in a lambda
Be extra careful when capturing data using lambdas or functors as only the sender will automatically control the lifetime of the connection. Consider the following case:
What if
data
has been deleted prior to emitting the signal? The connection might still be alive (if it hasn't been manually disconnected) and we'll have a naughty bug in our application. However, Qt provides another overload of the connect()
function where it's possible to provide a context object. This is used like so:Notice the third argument. We're now also providing the same captured object as a context object. If the context object is deleted, the connection will automatically disconnect. Note that the context object has to inherit from
QObject
for this to work.We've now come to an end of the basics. There should be enough information here to cover the most common cases. But down the development road you might hit some issues and will need some additional information in the future. Perhaps the next section will help you at those times.
Tips and tricks
- In case you have multiple overloads of a signals or slots you'll need to perform a cast within the
QObject::connect()
, e.g.:
- Although it should be avoided, it's possible to delete a listener (or schedule it for deletion) within its slot by using deletelater().
- There are several different types of connections. The type is defined by the fifth argument in the
connect()
-function. The type is an enum called Qt::ConnectionType and is defaulted toQt::AutoConnection
. In a single threaded application, the default behaviour is for the signal to directly notify the listeners. However, in a multi threaded application, usually the connections between the threads are queued up on each respective event loop. Threads are outside the scope of this series but is very well documented by Qt. - It's possible to connect a signal directly to another object's signal. This can be used, for example, to forward signals:
- You might find a different syntax that's used for the
connect()
function by other tutorials, blogs or even the Qt documentation, see code below. This style was the main syntax used up until Qt5. The syntax shown in this post provides several benefits over the old style. Personally, I believe the best advantage is that it allows compile-time checking of the signals and slots, which wasn't previously possible. However, in some cases the old syntax must be used, for example when connecting C++ functions to QML functions.
- Do you remember the
slots
keyword above (when defining the slot-functions)? It's actually only necessary to define when using the oldconnect()
syntax mentioned in the previous point. The type checking for the old syntax is done at run-time by using type introspection. The MOC generates the introspection code, but only if theslots
keyword is defined. - If you need to know which signals and slots are connected to a specific object at a certain point in time, you'll find QObject::dumpObjectInfo() very helpful. It's especially useful when debugging since it will output all the inbound and outbound signals for the object.
Back to the game architecture
You might now have a better understanding on how signals and slots can be used to separate the different game components. For example, let's say that we define four components which should be decoupled: the MouseComponent, the UnitComponent, the AIComponent and lastly the EnemyComponent. Furthermore, we'll use signals in each component to communicate and notify the other components. However to solve the separation, we'll have to introduce another component, the MainController, which will be used to create all the connections and define the game flow. The MainController will obviously need to depend on the different components, however the components are now well isolated from each other and we've achieved our goal. Do you see how this can be used in a GUI application to separate the visuals and user interaction from the actual logic?
Lastly, if you're interested in reading more about the inner-works of the signals and slots mechanism woboq.com has written a great blog series which has got you covered. Let me know if something is unclear or perhaps if I missed something out.
See you next time!
Qt5 Signal Slot Arguments ExamplesI basically have multiple events signals which I want to connect to the same slot. What I want to know is how can I pass string based parameters to that same slot so that the slot knows which is this signal coming from. One alternative is to make as many slots as there are signals and then connect them in a 1:1 manner, but this is efficient, considering that the code for all the processing is very similar. I tried doing this but I'm getting some errors:
connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString))); connect(button1,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade('button1'))); connect(button2,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade('button2'))); |
The error is related to the parameters I'm passing in the last 2 commands . And backgroundTypeChoiceMade is declared like this:
Can someone tell me what the error is in the above code ?
You can use QSignalMapper. Although the QSignalMapper is the answer to your question, I think jon hanson's answer is the way you should take. You get much more cleaner code that way.
Discussion
- I think in this case QSignalMapper is the way to go. Imagine changing buttons to QComboBox or any other UI change. With QSignalMapper, trivial change in a .cpp file. With separate slots, class signature change needed.
Qt5 Signal Slot Arguments 3
Four methods. One doesn't suck.
QSignalMapper . Works, but makes for messy code.- Named slots. Messy for any significant number of senders, and doesn't work for dynamically-generated senders (e.g., buttons in a list).
sender() -compare. Can handle dynamic senders, but is still kinda ugly.- Subclass the sender. Doesn't suck. Gives you what you really wanted all along: parameterized signals.
Especially when you're using a small number of signals and sender types and when the senders are dynamically generated, subclassing the sender is the cleanest way. This lets you overload the existing signals to contain whatever parameters you need.
And now, wiring up the signals and slots just works:
Keypad::Keypad(QWidget *parent): QWidget(parent) { for(int i =0; i <10;++i) { // KeypadButton keeps track of the identifier you give it buttons[i]=new KeypadButton(i, this); // And passes it as a signal parameter. Booyah. connect(buttons[i], SIGNAL(clicked(int)), this, SIGNAL(digitClicked(int))); } createLayout(); } void Keypad::digitClicked(int digit) { // The slot can find the clicked button with ease: dial(button[i]);// or whatever //.. } |
and the extra code is out-of-sight in a subclass you'll never have to touch again.
See http://doc.qt.digia.com/qq/qq10-signalmapper.html#thesubclassapproach for an example implementation of subclassing QPushButton to emit clicked(int) signals. Also discusses all four methods - named slots ('the trivial solution'), sender(), subclassing, and signal mapper.
Caveat: Obviously works best for small numbers of sender types. But that's usually the case. And in that case, it's worth it.
Discussion
- I think you should avoid QSignalMapper every time you (reasonably) can. The best solutions (which lead to cleaner code) are: 1) subclass and send your desired ID in the signal as @Foamy wrote. We use this in our Qt project whenever we can (but it's not a good idea to subclass just to do it) And 2) get the sender() in your slot, typecheck it and compare with whatever ID/tag you want and the sender has.
- @ViktorBenei I agree, except I do think it's worth it to subclass just for parameterized signals. It's a little more work in subclassing, but keeps the rest of your code that much cleaner.
What is inefficient about using separate slots? If there's commonality in the slot handlers then move that into a function, e.g. extending ereOn's example:
void YourClass::YourClass(): m_button1(new QPushButton()), m_button2(new QPushButton()) { connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot1())); connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot2())); } void YourClass::common(int n) { } void YourClass::yourSlot1() { common (1); } void YourClass::yourSlot2() { common (2); } |
Discussion
- Imagine if UI needs to be changed in arbitrary way, and compare needed code changes with this approach and QSignalMapper approach.
- And what if your UI changes at runtime? No way, José.
You can't pass constants to connect() because the effective parameters are deduced at execution time, not compile time.
However, while this is against the OO principle, you can use QObject::sender() which gives a pointer to the emitter QObject .
Example below:
void YourClass::YourClass(): m_button1(new QPushButton()), m_button2(new QPushButton()) { connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot())); connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot())); } void YourClass::yourSlot() { if((QPushButton* button =dynamic_cast<QPushButton*>(sender())) { // Now button points to a QPushButton* that you can compare with the pointers you already have if(button m_button1) { // Whatever }else if(button m_button2) { // Whatever } } } |
If you have many buttons, you may also use a QSignalMapper by providing an identifier for each button.
Discussion
- One could also use the objectName-property: doc.trolltech.com/4.5/qobject.html#objectName-prop
You can now really bind a value when connecting. Qt5 added support for that.
Example:
connect(sender, &Sender::valueChanged, tr1::bind(receiver, &Receiver::updateValue, 'senderValue', tr1::placeholder::_1)); |
See more info.
Slot translated to danish. NB: you can of course use std::bind or boost::bind instead of tr1::bind.
If you really don't want to use QSignalMapper, you could do something like this:
class SignalForwarderWithString:public QObject { Q_OBJECT public: SignalForwarderWithString(QString data =', QObject *parent =0): QObject(parent), _data(data){} QString _data; signals: void forward(QString); public slots: void receive(){ emit forward(_data);} }; .. connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString))); SignalForwarderWithString *sfws; sfws =new SignalForwarderWithString('button1', this); connect(button1,SIGNAL(clicked()), sfws, SLOT(receive(QString))); connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString))); sfws =new SignalForwarderWithString('button2', this); connect(button2,SIGNAL(clicked()), sfws, SLOT(receive(QString))); connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString))); |
but QSignalMapper is just as easy..
QSignalMapper *mapper =new QSignalMapper(this); connect(button1, SIGNAL(clicked()), mapper, SLOT(map())); mapper->setMapping(button1, 'button 1'); connect(button2, SIGNAL(clicked()), mapper, SLOT(map())); mapper->setMapping(button2, 'button 2'); // you might have to tweak the argument type for your slot.. connect(mapper, SIGNAL(mapped(const QString &), this, SLOT(backgroundTypeChoiceMade(QString))); |