Using Qt signals and slots vs calling a method directly
Asked Answered
M

5

16

Lets say I have a main window with a slider and a widget inside that window with a method called setValue(int). I'd like to call this method every time the value of the slider has changed.

Is there any practical difference between the two following ways of achieving it:

1

void MainWindow::on_slider_valueChanged(int value)
{
    ui->widget->setValue(value);
}

2

// somewhere in constructor
connect(ui->slider, SIGNAL(valueChanged(int)), ui->widget, SLOT(setValue(int)));

For me the first approach looks better, because it possibly avoids some overhead related to signals and slots mechanism and also, allows me to process the value before sending it to widget, if there's a need for it.

Are there any scenarios where the second solution is better?

Manufacturer answered 19/2, 2014 at 23:56 Comment(5)
And how do you get MainWindow::on_slider_valueChanged called? Thats the point. Signals provide an abstraction of events and their handlers. Dont care about performance (atleast if it affects your design) if you dont have a bottleneck, especially not with GUI code. GUI code is absolutely not performance relevant.Kenwrick
@Paranaix: MainWindow::on_slider_valueChanged is called because QMetaObject::connectSlotsByName() automatically generates this connection. Performance is not my concern here, but the design is. I'm trying to find out why one is better than the other, if it makes any difference at all.Manufacturer
One definite advantage of the signals-and-slots method is that signal/slot connections are automatically and safely disconnected whenever either of their endpoints is deleted. That means no risk of crashing due to access of a dangling pointer (e.g. if you did a "delete ui->widget" somewhere, your next call to on_slider_valueChanged() would crash, but example 2 would continue to run without errors)Ossieossietzky
The second approach is definitely better. In the first one, you introduce one more entity (on_slider_valueChanged) just to invoke some decent line of code. Moreover, those who will read your code are now have to scroll through all of micro-methods just to figure out the connections scheme.Mareah
In my opinion, it's the other way round. The second one is more direct - it connects directly signal with slot. The first one is indirect - you connect signal to your custom handler, which in turn invokes slot. First one doesn't "avoids some overhead related to signals and slots", on contrary, it adds some overhead.Sorilda
R
4

The main difference, in your example, of using a signal instead of a direct call, is to allow more than one listener.

If you directly call your widget setValue(), then only that one widget will receive the C++ signal.

If you use a Qt signal, now any other object can connect to receive the event whenever it occurs.

If you do not foresee any other object to ever want to receive the value by signal, I would not bother with such. A direct call is definitively a lot faster (between 3 and 6 CPU instructions instead of dealing with strings to find receivers!), but as Paranaix mentioned, in a GUI it may not be much of an issue (although in this case it could become a problem on older computers if you send all those signals while moving the sliderbar.)

Repository answered 20/2, 2014 at 1:30 Comment(5)
AFAIK the dealing with strings to find receivers happens only during the connect() and disconnect() calls, not during the emit-ing of the signals.Ossieossietzky
@JeremyFriesner And on Qt5 with new-style signal/slot connections, there's no dealing with strings at all :)Deforce
There is no direct call. Both cases use signal-slot connections.Deforce
There is a direct call, despite the fact that both cases use signal-slot mechanism.Manufacturer
You didn't mentioned the big advantage of the mechanism which is handling multithreading, so -1 from me. Dividing into GUI thread and Working thread is one of main purposes of signals&slots.Periphrasis
H
15

Both approaches use signal-slot connections. In the first case, the connect call is made by QMetaObject::connectSlotsByName() called from setupUi. In the second case, you explicitly call connect yourself.

Also, the first approach is unnecessary in Qt5 when using C++11. You can modify the value in a lambda:

QObject::connect(ui->slider, &QAbstractSlider::valueChanged,
                 [this](int val){ ui->widget->setValue(val*2); });

To protect from deletion of ui->widget, you should use a QPointer:

class MyWindow : public QMainWindow {
  QPointer<QAbstractSlider> m_widget;
  ...
public:
  MyWindow(QWidget * parent = 0) : QMainWindow(parent) {
    ...
    setupUi(this);
    m_widget = ui->widget;
    QObject::connect(ui->slider, &QAbstractSlider::valueChanged, 
                    [this](int val)
    {
      if (!m_widget.isNull()) m_widget->setValue(val*2); 
    });

The overhead of signal-slot connections is quantified in this answer.

Harewood answered 20/2, 2014 at 10:43 Comment(2)
Sorry, your answer seems impressive but didn't you made a mistake on 'QPointer<QAbstractSlider>' ? I think it's rather a pointer on the inside of the window (on MyWindow?)Cohobate
@Cohobate You're correct in that the pointer is owned by the instance of MyWindow. But it is a pointer to a QAbstractSlider that gets instantiated in setupUi. The pointer is type-safe - you couldn't make it point to a QMainWindow etc.Deforce
M
7

Signal/slot advantages:

  • multiple slots can be connected to single signal, and you don't bother with allocating and freeing memory for this
  • you can handle multithreading with this

Signal/slot drawbacks:

  • a little slower than direct call
  • significantly slower if the slot is virtual
  • QObject is rather heavy thing, so you usually try to avoid constructing billions of them

More details are available here

Mareah answered 20/2, 2014 at 15:4 Comment(1)
+ for mentioning multithreading. Slot might be invoked in different thread than signal and this is huge (maybe even best) advantage of this mechanism.Periphrasis
R
4

The main difference, in your example, of using a signal instead of a direct call, is to allow more than one listener.

If you directly call your widget setValue(), then only that one widget will receive the C++ signal.

If you use a Qt signal, now any other object can connect to receive the event whenever it occurs.

If you do not foresee any other object to ever want to receive the value by signal, I would not bother with such. A direct call is definitively a lot faster (between 3 and 6 CPU instructions instead of dealing with strings to find receivers!), but as Paranaix mentioned, in a GUI it may not be much of an issue (although in this case it could become a problem on older computers if you send all those signals while moving the sliderbar.)

Repository answered 20/2, 2014 at 1:30 Comment(5)
AFAIK the dealing with strings to find receivers happens only during the connect() and disconnect() calls, not during the emit-ing of the signals.Ossieossietzky
@JeremyFriesner And on Qt5 with new-style signal/slot connections, there's no dealing with strings at all :)Deforce
There is no direct call. Both cases use signal-slot connections.Deforce
There is a direct call, despite the fact that both cases use signal-slot mechanism.Manufacturer
You didn't mentioned the big advantage of the mechanism which is handling multithreading, so -1 from me. Dividing into GUI thread and Working thread is one of main purposes of signals&slots.Periphrasis
M
1

I prefer the second method, since it happened that I forgot to delete the "auto-connect-slots" when the UI-element was removed, causing dead code. AFAIK it is the same "behind the scene" (look at the auto-generated qt-files).

When you would like to modify the value I would prefer following method:

connect(ui->slider, SIGNAL(valueChanged(int)), this, SLOT(myOwnSlot(int)));

void MainWindow::myOwnSlot(int value) {
    /** do stuff */
    ui->widget->setValue(value);
}

Greetz

Madriene answered 20/2, 2014 at 11:0 Comment(0)
B
1

Signals & Slots is a different coding style. You can do things with signals that can be useful and neat work arounds to traditional c++. For example you can emit const signals from const functions and have them connect to non const slots (where as in c++ you cannot make non const calls from a const function). I've never liked using mutable objects, so signals provide a clean work around for me.

Bibelot answered 4/4, 2018 at 18:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.