Using Qt to communicate between interfaces

Sketch

The function of the interface is to provide a way to interact with other systems. Other systems don't need (or can't) know the details inside, but can only communicate with each other through the interface provided outside.

Pure virtual functions (slots are no exception) are easy to understand, so what about signals?

Is it valid to define a pure virtual signal in Qt?

Indeed, this topic is very interesting... Usually, we will define some pure virtual slot functions, but the topic of pure virtual signal is seldom discussed. So, can the signal be pure and imaginary?

Copyright: Once you go to Meng San Li, please indicate the source of the reprint. http://blog.csdn.net/liang19890820

Some assumptions about pure emptiness

Some understandings about signal and pure virtual:

  • Signals will never be implemented (that is, signals are defined in. h and not required in. cpp)
  • The main purpose of declaring a pure virtual function is to force inherited classes to provide an implementation.

Signals are not implemented. If they are declared to be pure virtual, they need inherited classes to provide an implementation, which directly conflicts with the "signal is not implemented". It's like having one person in two places at the same time. It's impossible. Therefore, it seems to be a mistake to declare a pure false signal.

In such programming, what if the interaction between signal slots is to be done? To be able to interact, most people may write code similar to the following:

Regrettably, Qt issued a warning:

warning: Signals cannot be declared virtual

So, how to solve this problem?

Solution

Here are three solutions:

  • Derived from QObject, signals do not use virtual
  • Define "signal" as a pure virtual function
  • Define the connection mode of the signal slot in the interface

The test results are as follows:

Derived from QObject, signals do not use virtual

Let's not use virtual, well, that's not necessary, this is the simplest solution!

#ifndef CLOCK_H
#define CLOCK_H

#include <QObject>

class IClock : public QObject
{
    Q_OBJECT

public:
    ~IClock() {}
    virtual void doSomething() = 0;

signals:
    void alarm();
};

#endif // CLOCK_H

Specific implementation is also independent of the signal, only need to achieve other interfaces can:

#ifndef DIGITAL_CLOCK_H
#define DIGITAL_CLOCK_H

#include "clock.h"
#include <QObject>
#include <QtDebug>

class DigitalClock : public IClock
{
    Q_OBJECT

public:
    DigitalClock() {}

    void doSomething() Q_DECL_OVERRIDE {
        qDebug() << "Do something...";
    }
};

#endif // DIGITAL_CLOCK_H

But this approach is not ideal, because in general, the interface is a simple class and does not need to be derived from QObject.

Define "signal" as a pure virtual function

"Signal" is defined as a pure virtual function:

#ifndef CLOCK_H
#define CLOCK_H

class IClock
{
public:
    ~IClock() {}
    virtual void doSomething() = 0;

    // Not a signal (but can be used as a signal), because IClock is not QObject
    virtual void alarm() = 0;
};

#endif // CLOCK_H

Note: For IClock, alarm() is a pure virtual function, not a "signal" (but can be used as a signal), because IClock is not a QObject.

Specific implementations need to be derived from QObject (because signals are needed), and alarm() needs to be defined as a signal:

#ifndef DIGITAL_CLOCK_H
#define DIGITAL_CLOCK_H

#include "clock.h"
#include <QObject>
#include <QtDebug>

class DigitalClock : public QObject, public IClock
{
    Q_OBJECT

public:
    DigitalClock() {}

    void doSomething() Q_DECL_OVERRIDE {
        qDebug() << "Do something...";
    }

signals:
    // Implementation is done by moc
    void alarm() Q_DECL_OVERRIDE;
};

#endif // DIGITAL_CLOCK_H

At this time, the specific implementation of alarm() is accomplished by moc internally.

To test the validity, define a class - monitor.h:

#ifndef MONITOR_H
#define MONITOR_H

#include <QObject>
#include <QtDebug>
#include "clock.h"

class Monitor : public QObject
{
    Q_OBJECT

private slots:
    void onAlarm() {
        qDebug() << "Get up";
    }

public:
    void monitorAlarm(IClock *clock) {
        QObject *clockObject = dynamic_cast<QObject *>(clock);
        if (clockObject) {
            connect(clockObject, SIGNAL(alarm()), this, SLOT(onAlarm()));
        } else {
            qWarning() << "cannot monitor Alarm";
        }
    }
};

#endif // MONITOR_H

Note: When connecting signals, you need to convert them to QObject.

Testing in main.cpp:

#include <QCoreApplication>
#include "digital_clock.h"
#include "monitor.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    DigitalClock clock;
    Monitor monitor;
    monitor.monitorAlarm(&clock);
    emit clock.alarm();

    return a.exec();
}

Define the connection mode of the signal slot in the interface

In addition to the above, the connection mode of the signal slot can also be defined in the interface.

First, define an interface to connect the signal slot - connectToAlarm():

#ifndef CLOCK_H
#define CLOCK_H

#include <QObject>

class IClock
{
public:
    ~IClock() {}
    virtual void doSomething() = 0;
    virtual bool connectToAlarm(QObject *receiver, const char *pszSlot, bool isConnect) const = 0;
};

#endif // CLOCK_H

Then, the specific connection of the signal slot is realized in the derived class:

#ifndef DIGITAL_CLOCK_H
#define DIGITAL_CLOCK_H

#include "clock.h"
#include <QObject>
#include <QtDebug>

class DigitalClock : public QObject, public IClock
{
    Q_OBJECT

public:
    DigitalClock() {}

    void doSomething() Q_DECL_OVERRIDE {
        qDebug() << "Do something...";
    }

    bool connectToAlarm(QObject *receiver, const char *pszSlot, bool isConnect) const Q_DECL_OVERRIDE{
        if (isConnect)
            return connect(this, SIGNAL(alarm()), receiver, pszSlot);
        return disconnect(this, SIGNAL(alarm()), receiver, pszSlot);
    }

signals:
    void alarm();
};

#endif // DIGITAL_CLOCK_H

Since then, the connection has become much simpler.

#ifndef MONITOR_H
#define MONITOR_H

#include <QObject>
#include <QtDebug>
#include "clock.h"

class Monitor : public QObject
{
    Q_OBJECT

public slots:
    void onAlarm() {
        qDebug() << "Get up";
    }

public:
    void monitorAlarm(IClock *clock) {
        bool isConnect = clock->connectToAlarm(this, SLOT(onAlarm()), true);
        if (isConnect) {
            qWarning() << "connectToAlarm";
        } else {
            qWarning() << "cannot monitor Alarm";
        }
    }
};

#endif // MONITOR_H

Keywords: Qt Programming

Added by ednark on Mon, 20 May 2019 20:57:11 +0300