My signal / slot connection does not work

21,831

Solution 1

There are some rules that make life with signals and slots easier and cover the most common reason for defective connections. If I forgot something please tell me.

1) Check the debug console output:

When execution errors occur, the debug output can show you the reason.

2) Use the full signature of signal and slot:

Instead of

connect(that, SIGNAL(mySignal), this, SLOT(mySlot));

write

connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));

and check your spelling and capitalization.

3) Use existing overloads:

Carefully check if you are using the desired overloads of signal and slot and if the overloads you used actually exist.

4) Your signal and slot must be compatible:

This especially means the parameters must be of the same type (references are tolerated) and have the same order.

Compile-time syntax also needs the same number of parameters. Old runtime syntax allows connecting signals to slots with less parameters.

5) Always check return value of connect method (programmers should never ignore return values):

Instead of

connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));

always use something like

bool success = connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
Q_ASSERT(success);

Or if you like throw an exception or implement full error handling. You may also use a macro like that:

#ifndef QT_NO_DEBUG
#define CHECK_TRUE(instruction) Q_ASSERT(instruction)
#else
#define CHECK_TRUE(instruction) (instruction)
#endif 

CHECK_TRUE(connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int))));

6) You need an event loop for queued connections:

I.e. when ever you connect signals/slots of two objects owned by different threads (so called queued connections) you need to call exec(); in the slot's thread!

The event loop also needs to be actually served. Whenever the slot's thread is stuck in some kind of busy loop, queued connections are NOT executed!

7) You need register custom types for queued connections:

So when using custom types in queued connections you must register them for this purpose.

First declare the type using the following macro:

Q_DECLARE_METATYPE(MyType)

Then use one of the following calls:

qRegisterMetaType<MyTypedefType>("MyTypedefType"); // For typedef defined types
qRegisterMetaType<MyType>(); // For other types

8) Prefer new compile time syntax over old run-time checked syntax:

Instead of

connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));

use this syntax

connect(that, &ThatObject::mySignal, this, &ThisObject::mySlot));

which checks signal and slot during compile time and even does not need the destination being an actual slot.

If your signal is overloaded use the following syntax:

connect(that, static_cast<void (ThatObject::*)(int)> &ThatObject::mySignal), this, &ThisObject::mySlot); // <Qt5.7
connect(that, qOverload<int>::of(&ThatObject::mySignal), this, &ThisObject::mySlot); // >=Qt5.7 & C++11
connect(that, qOverload<int>(&ThatObject::mySignal), this, &ThisObject::mySlot); // >=Qt5.7 & C++14

Starting with Qt5.14, overloaded signals are deprecated. Disable deprecated Qt features to get rid of the above shenanigans.

Also do not mix const/non-const signals/slots for that syntax (normally signals and slots will be non-const).

9) Your classes need a Q_OBJECT macro:

In classes where you are using "signals" and "slots" specifications you need to add a Q_OBJECT macro like this:

class SomeClass
{
   Q_OBJECT

signals:
   void MySignal(int x);
};

class SomeMoreClass
{
   Q_OBJECT

public slots:
   void MySlot(int x);
};

This macro adds necessary meta information to the class.

10) Your objects must be alive:

As soon as either the sender object or the receiver object is destroyed, Qt automatically discards the connection.

If the signal isn't emitted: Does the sender object still exist? If the slot isn't called: Does the receiver object still exist?

To check the lifetime of both objects use a debugger break point or some qDebug() output in the constructors/destructors.

11) It still does not work:

To do a very quick and dirty check of your connection emit the signal by your self using some dummy arguments and see if it is called:

connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
emit that->mySignal(0); // Ugly, don't forget to remove it immediately

Finally of course it is possible that the signal simply is not emitted. If you followed the above rules, probably something is wrong in your program's logic. Read the documentation. Use the debugger. And if there is now other way, ask at stackoverflow.

Solution 2

In my practice, I have encountered cases of incorrectly overriding eventFilter in the object receiving the signal. Some novice programmers forget to return "false" at the end of function. And thus do not allow the MetaCall event to pass to the receiving object. In this case, the signal is not processed at the receiving object.

Solution 3

Short answer

You (almost) don't have to worry about that anymore. Always use the QMetaMethod/Pointer to member prototype of connect, as it will fail at compile time if the signal and slot are not compatible.

connect(sourceObject, &SourceClass::signal, destObject, &DestClass::slot);

This prototype will only fail at runtime if the sourceObject or destObject is null (which is to be expected). But argument incompatibility will show up during compilation

Only rare situations require the older SIGNAL/SLOT literal-based syntax, so this should be your last resort.

Compatibility

The signatures are compatible if the following conditions are met:

  • You are connecting a signal to a slot or a signal
  • The destination signal/slot has the same number or less arguments than the source signal
  • Arguments of the source signal can be implicitly converted to the corresponding argument (matched in order) in the destination signal/slot, if used
Examples
  • OK - signalA(int, std::string) => signalC(int, std::string)
    • Note that we are connecting to a signal
  • OK - signalA(int, std::string) => slotB(int, std::string)
  • OK - signalA(int, std::string) => slotB(int)
    • String parameter ignored
  • OK - signalA(int, std::string) => slotB()
    • All parameters ignored
  • OK - signalA(int, const char*) => slotB(int, QString)
    • Implicitely converted with QString(const char*)
  • Fails - signalA(int, std::string) => slotB(std::string)
    • int not implicitely convertible to std::string
  • Fails - signalA(int, std::string) => slotB(std::string, int)
    • Incorrect order
  • Fails - signalA(int, std::string) => slotB(int, std::string, int)
    • Too many arguments on the right side
Share:
21,831
Silicomancer
Author by

Silicomancer

28 things only developers will find funny

Updated on December 30, 2020

Comments

  • Silicomancer
    Silicomancer over 3 years

    I repeatedly see people having problems with slots not being called. I would like to collect some of the most common reasons. So maybe I can help people and avoid a lot of redundant questions.

    What are reasons for signal / slot connections not working? How can such problems be avoided?

  • thuga
    thuga over 9 years
    You don't really have to declare your custom type with Q_DECLARE_METATYPE if you're planning on using it in a queued slot. qRegisterMetatype should be enough. Q_DECLARE_METATYPE is only needed for template-based functions. There was a short discussion about this here.
  • Silicomancer
    Silicomancer over 9 years
    Hm. It seems QVariant is not direct part of queued connections mechanism. However documentation of qRegisterMetaType() says: "Call this function to register the type T. T must be declared with Q_DECLARE_METATYPE()".
  • Hareen Laks
    Hareen Laks about 6 years
    Need to thoroughly examine the typos, since compiler not giving any compilation errors at the connection.
  • Silicomancer
    Silicomancer about 6 years
    @HareenLaks: See above, "check your spelling and capitalization"
  • Tobi
    Tobi over 3 years
    Excellent summary. Thank you.
  • Wade Wang
    Wade Wang about 3 years
    tip: to check if there is spelling typo in connect(...), you can press CTRL key and move the mouse onto the signal or slot function, click left mouse button, if the IDE jump to the definition of the signal or slot function, it means the spelling is correct.
  • Louis Go
    Louis Go over 2 years
    For Qt4, signal is protected, so &ThatObject::mySignal doesn't work.. Hopefully no one step into this hole nowadays.