1
votes

I have a use case when user actions initialise application data at each runtime. To represent that behavior here is my sample code:

SignalSlotChange.h

#ifndef SIGNALSLOTCHANGE_H
#define SIGNALSLOTCHANGE_H

#include <QtGui>

class SignalSlotChange : public QWidget
{
Q_OBJECT

public:
    SignalSlotChange(QWidget *parent = 0);

private slots:
    void firstCall();
    void secondCall();

private:
    QPushButton *button;

};

#endif // SIGNALSLOTCHANGE_H

SignalSlotChange.cpp

#include "SignalSlotChange.h"

SignalSlotChange::SignalSlotChange(QWidget *parent) : QWidget(parent)
{
    button = new QPushButton("Messgage", this);
    QObject::connect(button, SIGNAL(clicked()), this, SLOT(firstCall()));
    show();
}

void SignalSlotChange::firstCall()
{
    QMessageBox::information(this, "SignalSlotChange", "First call", QMessageBox::Ok, QMessageBox::NoButton);
    // Change the signal-slot connection to secondCall()
    QObject::disconnect(button, SIGNAL(clicked()), this, SLOT(firstCall()));
    QObject::connect(button, SIGNAL(clicked()), this, SLOT(secondCall()));
}

void SignalSlotChange::secondCall()
{
    QMessageBox::information(this, "SignalSlotChange", "Second call", QMessageBox::Ok, QMessageBox::NoButton);
}


int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    SignalSlotChange ssc;
    return app.exec();
}

When the button is pressed then the initialising slot firstCall() is called. It changed the signal-slot connection to secondCall() for subsequent signals.

The problem with the way I do it is that it is highly coupled and requires that slot knows exact method and signals to change it.

With QObject::sender() I would know origin of sender but not its signal, and I would know about just one sender which happened to emit that signal.

I can do it with a Boolean first_call but that would be making a check on Boolean value for all subsequent calls, something which I want to avoid and hence this question.

1
Is your concern only about execution speed? If so, I would still choose the approach to leave the signal/slot connection intact and use a first_call boolean to distinguish between the first and all subsequent calls. Don't overestimate the "overhead" of checking a boolean - in a typical UI application, you will never ever get aware of this check. Might be different if you have some unique special requirements, of course ...Andreas Fester
In a right state of mind anyone would do that, that's why I mentioned it, to save people's time from putting that as answer. This question is asked from learning point of view, if the given application explicitly demands that.Xolve
Fair enough :) See my answer below for a somewhat different solution.Andreas Fester

1 Answers

1
votes

A somewhat different solution could be implemented by using a pointer-to-member approach:

SignalSlotChange.h

#ifndef SIGNALSLOTCHANGE_H
#define SIGNALSLOTCHANGE_H

#include <QtGui>

class SignalSlotChange : public QWidget {
Q_OBJECT


public:
    SignalSlotChange(QWidget *parent = 0);

private slots:
    void callCall();

private:
    void (SignalSlotChange::* delegate) (); 

    void firstCall();
    void secondCall();

    QPushButton *button;

};

#endif // SIGNALSLOTCHANGE_H

SignalSlotChange.cpp

#include "SignalSlotChange.h"

SignalSlotChange::SignalSlotChange(QWidget *parent) : QWidget(parent) {
    delegate = &SignalSlotChange::firstCall;

    button = new QPushButton("Messgage", this);
    QObject::connect(button, SIGNAL(clicked()), this, SLOT(callCall()));
    show();
}

void SignalSlotChange::callCall() {
    (this->*delegate) ();
}

void SignalSlotChange::firstCall() {
    QMessageBox::information(this, "SignalSlotChange", "First call", QMessageBox::Ok, QMessageBox::NoButton);

    // Change the effective signal-slot connection to secondCall()
    delegate = &SignalSlotChange::secondCall;
}

void SignalSlotChange::secondCall() {
    QMessageBox::information(this, "SignalSlotChange", "Second call", QMessageBox::Ok, QMessageBox::NoButton);
}


int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    SignalSlotChange ssc;
    return app.exec();
}

I am not sure if this is better solution from a decoupling perspective, but the advantage is that the mechanism does not need to know anything about the slot which was actually triggered. The complete logic to implement the "method switch" is encapsulated within the SignalSlotChange class. The callCall() slot can be connected to any other compatible signal and the method switch still works, with no further code changes.