0

I'm new to QT and I want to ask:

How can I declare signal from interface class, later emit it in another subclass and connect to slot? I found: connect(dynamic_cast<QObject*>(obj_typeof_interface), SIGNAL(signal), this, SLOT(slot)); but doesn't work...

Complete code:

My interface

class InstrumentsCommunication : public QObject
{
    Q_OBJECT
public:
    virtual bool open(const std::string addr) = 0;
    virtual void transaction(Command cmd, qint16 rate) = 0;

signals:
    virtual void getData(const QString s) = 0;

private:
    virtual void write(const std::string cmd) = 0;
};

Q_DECLARE_INTERFACE(InstrumentsCommunication, "InstrumentsCommunication")

the implementation

class SerialCommunication : public InstrumentsCommunication
{
    Q_OBJECT
    Q_INTERFACES(InstrumentsCommunication)
public:
    SerialCommunication();
    bool open(const std::string addr) override;
    void transaction(Command cmd, qint16 rate) override;

signals:
    void getData(const QString s) override;

public slots:
    void read();

private:
    QSerialPort serial;
    QString buffer;
    void write(const std::string cmd) override;
};

//Methods implementations

void SerialCommunication::read(){
    buffer += serial.readAll();

    if(buffer.contains("\r\n")){
        qDebug() << "read()";
        emit this->getData(buffer.trimmed());
        buffer = "";
    }
}

I have implemented Facory Method...

Creator class

class Creator : public QObject{
    Q_OBJECT
public:
    virtual ~Creator(){};
    virtual InstrumentsCommunication* FactoryMethod() const = 0;
    bool open(const std::string addr);
    void write(Command cmd);

signals:
    void getData(const QString s);

public slots:
    void recvData(const QString s);

private:
    InstrumentsCommunication* instrComm;
};


bool Creator::open(const std::string addr){
    instrComm = this->FactoryMethod();
    //Doesn't work
    //QObject::connect(instrComm, &InstrumentsCommunication::getData, this, &Creator::recvData);

    //Doesn't work
    //QObject::connect(dynamic_cast<QObject*>(instrComm), SIGNAL(getData), this, SLOT(recvData));

    bool isConn = instrComm->open(addr);
    return isConn;
}

//...

void Creator::recvData(const QString s){
    qDebug() << "recv";
    emit this->getData(s);
}

All declaration classes are in different .h file

cle
  • 1
  • 1
  • It seems you completely misunderstood how signals, slots and connections work. Your code does not make any sense at all. Btw. signals should not have any implementation at all. At least not implementation written by user. Qt MOC generates signal body for you. You only declare a signal in the base class and then you can emit them from any derived class. – HiFile.app - best file manager Oct 01 '21 at 06:43
  • Thanks for the suggestion. But I didn't implement the signal; read() method in SerialCommunication Class emit it. I only implemented slots. – cle Oct 01 '21 at 07:38
  • I reacted to your original text (before you edited it), which said "later implement it in another subclass". You then edited word "implement" to "emit", which of course makes better sense. – HiFile.app - best file manager Oct 01 '21 at 09:45
  • I changed it after your comment, thanks. But in my code did I write something wrong? – cle Oct 01 '21 at 09:54

1 Answers1

1

There are a few things wrong. For example this line in InstrumentsCommunication definition is wrong:

signals: 
    virtual void getData(const QString s) = 0;

No need for virtual nor =0. Use only this:

signals: 
    void getData(const QString s); 

That is all you need. And do not override it in the derived class. Just delete these lines in SerialCommunication

signals:
    void getData(const QString s) override; 

Then your connection

QObject::connect(instrComm, &InstrumentsCommunication::getData, this, &Creator::recvData);

should work if you instantiate the objects correctly and call the methods in correct order, which is a part of your code which we can not see and therefore we do not know if it is correct. I would however recommend that you enforce uniqueness of the connection (the optional parameter in connect()) and assert the result of the connection to be true.

bool success = connect(instrComm, &InstrumentsCommunication::getData, this, &Creator::recvData, Qt::UniqueConnection);
Q_ASSERT(success); // assert you established correct connection
Q_UNUSED(success); // to suppress warning 'unused local variable' in release mode

I am using a macro/template for all my connections to assert they are unique (non-unique connections are almost always a mistake) and successful. I recommend it very much in all your Qt code. It will save you lots of troubles, believe me. See https://github.com/vladimir-kraus/qtutils/blob/main/qtutils/safeconnect.h

However, there are many more things which make it terrible Qt/C++ code style but they should not affect functionality so I do not mention them.

  • It doesn't work, object instantiation is throgh FactoryMethod() and it's `SerialCommunication* SerialCreator::FactoryMethod() const{ return new SerialCommunication(); }` in SerialCreator class which implements Creator class. FactoryMethod call is in Creator class. (see code above). Thanks again for the time. – cle Oct 01 '21 at 12:14
  • What do you mean by "it does not work". Does it compile? Does it fail when establishing connection? Or does fail calling slot after emitting signal? – HiFile.app - best file manager Oct 01 '21 at 12:19
  • I see potential problem in lifetime of `Creator` object. It depends when you delete this object. If you use only for creating of `SerialCommunication` object and delete it just afterwards, then of course the connection is automatically disconnected because the creator object does not exist so it cannot pass signals and call its own slots. But this is what I mean by "correct instantiation of objects". It depends on the rest of your code. – HiFile.app - best file manager Oct 01 '21 at 12:34
  • If you are defining an interface class then I think it's required to use the old signal and slot definition as well as an object cast: `connect(dynamic_cast(obj), SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));` Edit: [This question](https://stackoverflow.com/questions/32876713/connect-qt-signals-declared-in-interface) actually does what you are trying. – slayer Oct 02 '21 at 01:10