8

Debugging signals and slots can be hard, because the debugger does not jump to the slots of a signal when it is emitted. What are some best practices for debugging Qt signals and slots?

In particular

  1. How do I make sure connections are set up successfully?
  2. When should I use signals and slots, when should I avoid them?
  3. What are the most efficient debugging techniques from your experience?
Ralph Tandetzky
  • 22,780
  • 11
  • 73
  • 120

4 Answers4

9

There was a blog post written a while back called 20 ways to debug Qt signals and slots
It addresses I think #1 and #3 of your questions.

For #2, I don't think there is really a hard and fast reason to use or not use signals/slots, as they are a pretty core concept of the GUI framework. Signals are a perfect way to decouple the knowledge of one component to another, allowing you to design reusable widgets that simply declare state changes or notifications. It is also a very good way to communicate GUI changes from non-GUI thread loops, by emitting a signal that the main thread can see.

There might be times where what you really want instead of a signal/slot is to use events, such as when a parent widget should become the event filter for a number of child widgets. The children still do not need to know about the parent, and the parent gets a more direct event as opposed to a signal connection.

On the same topic of events, there are times where what you really want is a bubbling up of an event from child -> parent -> grandparent -> etc. Signals would make less sense here because they are not meant as a way to determine whether the proposed event should result in an action (they could be used this way obviously). Events allow you to check the current state, decide whether this widget should do anything, or otherwise bubble them up the chain for someone else to inspect.

There is a really fantastic answer about The Difference Between Signals/Slots and Events. Here is a good snippet:

  • You "handle" events
  • You "get notified of" signal emissions

What I like about that quote is that it describes the different need case. If you need to handle an action in a widget, then you probable want an event. If you want to be notified of things happening, then you probably want a signal.

Community
  • 1
  • 1
jdi
  • 90,542
  • 19
  • 167
  • 203
  • 5
    The link you provided is rather underwhelming in terms of actual debugging techniques. The gist is basically: *"Avoid writing bugs, and here are 20 things that can go wrong"*. No mention of tools or techniques to actually debug issues. When you get a crash dump, you are confronted with an insanely deep callstack, lots of `qt_static_metacall` invocations, no easy way to decipher the emitted signal, and simply no way of knowing, where any particular signal-slot-connection was set up. – IInspectable Sep 23 '15 at 07:47
2

In addition to what have been said, here are additional tricks.

If you use QTest for your unit tests, then you can pass -vs argument to the executable and all signals will be shown in the console.

I looked at how QTest works, and it registers callbacks that is triggered when signal and slots are executed using QSignalDumper class. However, this API is not exported and might break any time. Here is how I was able to hook on all signals and slots on Linux with Qt 5.10 using GCC.

// QSignalSpyCallbackSet is defined in qt5/qtbase/src/corelib/kernel/qobject_p.h

struct QSignalSpyCallbackSet
{
    typedef void (*BeginCallback)(QObject *caller, int signal_or_method_index, void **argv);
    typedef void (*EndCallback)(QObject *caller, int signal_or_method_index);
    BeginCallback signal_begin_callback,
                    slot_begin_callback;
    EndCallback signal_end_callback,
                slot_end_callback;
};
typedef void (*register_spy_callbacks)(const QSignalSpyCallbackSet &callback_set);

static void showObject(QObject *caller, int signal_index, const QString &msg)
{
   const QMetaObject *metaObject = caller->metaObject();
   QMetaMethod member = metaObject->method(signal_index);
   qDebug() << msg << metaObject->className() << qPrintable(member.name());
}

static void onSignalBegin(QObject *caller, int signal_index, void **argv)
{
   showObject(caller, signal_index, "onSignalBegin");
}

static void onSlotBegin(QObject *caller, int signal_index, void **argv)
{
   showObject(caller, signal_index, "onSlotBegin");
}

int main(int argc, char *argv[])
{
   static QSignalSpyCallbackSet set = { onSignalBegin, onSlotBegin, 0, 0 };
   QLibrary qtcore("libQt5Core");
   register_spy_callbacks reg = (register_spy_callbacks)qtcore.resolve("_Z32qt_register_signal_spy_callbacksRK21QSignalSpyCallbackSet");

   if (reg) {
      reg(set);
   }
   ...
}

I believe that Qt should expose that API, because we could use it for so many things beyond debugging, such as monitoring the time spent in a slot, get statistics, etc.

fgiraldeau
  • 2,384
  • 1
  • 19
  • 12
0

How do I make sure connections are set up successfully?

You will see warnings in the console output from your application for each failed connection.

When should I use signals and slots, when should I avoid them?

In my opinion, it's fine to use them any time you want to maintain separation of concerns in your class design. Your class can emit a signal that may or may not be answered by another class (or classes) completely unknown to your class. This keeps your coupling down.

What are the most efficient debugging techniques from your experience?

I can't really add anything more than what is said on this blog post. 20 ways to debug Qt signals and slots

kenrogers
  • 1,350
  • 6
  • 17
0

Regarding #1, I'll just add another piece of information that I didn't see mentioned above or in the referenced blog post.

From the documentation on QObject::connect():

Creates a connection of the given type from the signal in the sender object to the method in the receiver object. Returns true if the connection succeeds; otherwise returns false.

I prefer asserting the return value of my connection to make sure the connection succeeded, especially because not all Qt programs will have console output. This also leads to more easily maintainable code, as it will catch changes made at a later date to either the signal or the slot, and force the programmer who made the changes to update the connections as well.

user3288829
  • 1,266
  • 15
  • 26
  • I realize this is an ancient post, but just in case it gets used recently.The documentation actually states that connect returns "handle" . I believe checking for validity of the return - whatever it is - is more generic. –  Sep 02 '20 at 14:34
  • @JanHus The documentation I quoted was the Qt 4.8 version. In Qt5 a handle is returned, but even more importantly a new functor-based connection was added that allows for compile time checking, which is safer (see https://doc.qt.io/qt-5/signalsandslots-syntaxes.html). – user3288829 Sep 02 '20 at 14:52
  • I have been using QtDesigner GUI ( "edit slots/signals;" ) by moving the mouse around. It is limited and "builds" a funky GUI "string" .Since there is no code to check it probably does the older "connect" form anyway. Nice feature but hardly user friendly. –  Sep 03 '20 at 15:37