Signal/Slot between classes and Design of Classes - c++

suppose I've the following classes :
class A:public QObject{
Q_OBJECT
...
signals:
void sendData(QString data);
}
class B:public QObject{
Q_OBJECT
public:
A a;
...
public slots:
void onSendData(QString);
signals:
void sendData(QString data);
}
class C:public QObject{
Q_OBJECT
public:
B b;
...
public slots:
void onSendData(QString);
signals:
void sendData(QString data);
}
.
.
.
class MainWindow:public QMainWindow{
Q_OBJECT
public:
LastClass lc;
public slots:
void onSendData(QString);//show data on ui
}
class A digs data and when it finds a special data it must send it to ui(mainwindow) , so it calls sendData signal, then class B which contains an instance of A, grabs the signal and from it's slot sends it to above class ,...
as you can see, it causes a recursive signal/slot send and receiving which I doubt that it is a good design.
Is it a correct way or must I change the design ? ( it's hard to change design in some circumstances though).
(I cannot use inheritance because each class has a different functionality and different functions.)

This is not an obligation to connect the signals to a slot, in your case you can directly connect the signals between them. In the class B for example:
CONNECT(a, SIGNAL(sendData(QString)), this, SIGNAL(sendData(QString)));
Alternatively, you can use events so that you don't have to make this chain (but you won't know for sure who is catching the event then). Documentation here.

Related

Qt signal-slot duplication code avoidance

I would like to share signals and possibly slot implementations among different classes, but it seems Qt does not allow this.
Basically I would like to have something like:
class CommonSignals
{
signals:
void mysignal();
};
class A :
public QObject,
public CommonSignals
{
Q_OBJECT
public:
void doSomething()
{
emit mysignal();
}
};
class B :
public QObject,
public CommonSignals
{
Q_OBJECT
public:
B()
{
connect(&a, &A::mysignal, this, &B::mysignal);
}
A a;
};
So that when for some reason A emits a signal B emits the same signal too. This to avoid useless code replication and improve maintainability.
Any ideas?
PS I've tried also with virtual inheritance but I've got classical qmake problems
You cannot do this. QObject cannot be used with multiple inheritance of multiple QObject bases. Only the first inherited class can be a QObject. See:
https://doc.qt.io/qt-5/moc.html#multiple-inheritance-requires-qobject-to-be-first
Since you need both base classes to be a QObject (CommonSignals provides signals, it would need to be a QObject), you're out of luck. Your only option here is using plain old macros:
#define COMMON_SIGNALS \
void signal1(); \
void signal2();
class A: public QObject
{
Q_OBJECT
public:
// ...
signals:
COMMON_SIGNALS
};
class B: public QObject
{
Q_OBJECT
public:
// ...
signals:
COMMON_SIGNALS
};
The core issue with all this is that Qt uses moc to generate the underlying code for signals and slots. However, moc is just a simple preprocessor that doesn't understand most of C++.
You could use Verdigris to get rid of moc:
https://github.com/woboq/verdigris
This allows you to have templated QObject classes, for example. I have not tried it myself and thus don't know if it actually allows multiple inheritance. Might be worth looking into.
Why not just move the inheritance from QObject away from the derived classes A and B and into CommonSignals...
class CommonSignals: public QObject {
Q_OBJECT;
signals:
void mysignal();
};
class A: public CommonSignals {
Q_OBJECT;
public:
void doSomething ()
{
emit mysignal();
}
};
class B: public CommonSignals {
Q_OBJECT;
public:
B ()
{
connect(&a, &A::mysignal, this, &B::mysignal);
}
A a;
};
Will that not work for you?

Why does Qt default signals to be public?

I've realized today that if you have 3 classes like so:
class 1 has a signal
class 2 has a signal that connects to class 3's private slot
class 3 has a private slot and class 2 is a friend of class 3
If I connect class 1's signal to class 2's signal, I can essentially call class 3's private slot by emitting class 1's signal.
If signals were private or protected this wouldn't occur.
There's another scenario where this can be bad, imagine we have this setup:
class 1 has a signal
class 2 has a signal that connects into it's base classes protected slot
class 3 is the base class with a protected slot
Doesn't this break Object Oriented Principles?
woboq mentions this:
Signals were protected in Qt4 and before. They are becoming public in Qt5 in order to enable the new syntax.
You can create a private / protected helper object with the 'private' signal:
class Helper : public QObject
{
Q_OBJECT
public:
Helper(QObject *parent) : QObject(parent) { }
signals:
void MyPrivateSignal();
};
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass()
: QObject(),
helper(new Helper(this))
{
connect(helper, &Helper::MyPrivateSignal, this, &MyClass::MyPrivateSlot);
}
private slots:
void MyPrivateSlot();
private:
void InvokeMyPrivateSignal()
{
emit helper->MyPrivateSignal();
}
private: // or protected:
Helper *helper;
};
Anyone can connect to Helper::MyPrivateSignal() of a Helper instance, but not to your Helper instance, because only you know that one.

Qt property outside of base class

Im using a QML frontend for my C++ App which worked fine so far. However, I planned to tidy up my code and split functions into smaller classes
At first, my Property decleration looked like this:
class mainBoard : public QObject
{
Q_OBJECT
Q_PROPERTY(double baroAltitude MEMBER baroAltitude NOTIFY pressureChanged)
public:
explicit mainBoard(QObject *parent = 0);
void start();
private:
double baroAltitude = 0;
signals:
void pressureChanged();
};
Now, I do have this external class, with my getter method.
#include "pressuresensor.h"
class mainBoard : public QObject
{
Q_OBJECT
Q_PROPERTY(double baroAltitude READ pressureSensors.getBaroAltitude NOTIFY pressureSensors.pressureChanged)
public:
explicit mainBoard(QObject *parent = 0);
void start();
private:
pressureSensor pressureSensors;
};
But now, all I get is:
mainboard.h:25: Parse error at "pressureSensors"
error: [moc_mainboard.cpp] Error 1
Is there a better, or correct (because its working :D ) way for it?
thanks!
Q_PROPERTY does not support getters/setters methods which are not part of the class in question.
If you really want to keep the pressureSensor class you have to provide getters/setters in the mainBoard class and forward the calls.
class mainBoard : public QObject
{
Q_OBJECT
Q_PROPERTY(double baroAltitude READ getBaroAltitude)
public:
double getBaroAltitude() const {
return pressureSensors.getBaroAlitude();
}
private:
pressureSensor pressureSensors;
};

Override Qt slot in subclass

I have a base class, which defines a Qt slot
class Base
{
public:
Base()
{
connect(otherobject, SIGNAL(mySignal), this, SLOT(mySlot));
}
public slots:
virtual void mySlot()
{}
}
Subclass A just implements some other stuff. Subclass B overrides the slot
class SubB : Base
{
public:
SubB() : Base()
{
// Necessary?
connect(otherobject, SIGNAL(mySignal), this, SLOT(mySlot));
}
public slots:
virtual void mySlot() override
{}
}
Does the override of the slot also replace the connection, which was done before in the Bass constructor (I.e. The connect in SubB would be unnecessary)?
It gets better: you don't need any special treatment for the slot in the derived class. There's no need to make it virtual (it already is per C++ semantics), and there's no need to make it a slot again (it already is per Qt semantics). It is incorrect to add the second connection in Derived, it'll simply result in the slot being activated twice per every activation of the signal.
Remember that signals and slots are regular C++ methods, and that slots are invoked from machine-generated code that looks exactly as if you called the slot without specifying the particular class it should be in. Thus a virtual slot acts as you think it should, given the semantics of C++.
The below is sufficient:
class Base : public QObject
{
Q_OBJECT
public:
Base(QObject * src, QObject * parent = 0) : QObject(parent)
{ connect(src, SIGNAL(mySignal), SLOT(mySlot)); }
Q_SLOT virtual void mySlot() {}
};
class Derived : public Base
{
Q_OBJECT
public:
Derived(QObject * src, QObject * parent = 0) : Base(src, parent) {}
void mySlot() Q_DECL_OVERRIDE { ... }
};
You don't need to put the same connect in the subclass constructor. When a SubB object is created, the connect in the Base constructor will connect the right slot (the one overridden in SubB).

Qt signals and slots trouble

I have 2 classes:
class Server : public QTcpServer
{
Q_OBJECT
public:
Server(QObject * parent = 0 , quint16 port = 1922);
void SendData(QString data);
virtual ~Server();
signals:
void RecieveMessage(QString);
private slots:
void acceptConnection();
void startRead();
void disconnected();
private:
QTcpServer *tcpServer;
QTcpSocket *client;
};
and
class ChessLanTEst : public QMainWindow
{
Q_OBJECT
public:
friend class Server;
friend class Client;
ChessLanTEst(QWidget *parent = 0);
~ChessLanTEst();
private:
Ui::ChessLanTEstClass ui;
Server *server_;
Client *client_;
private slots:
void createGame();
void ShowMessage(QString);
};
in Server class I have signal:
void RecieveMessage(QString);
in ChessLanTEst class I have slot:
void ShowMessage(QString);
and I connected it in the ChessLanTEst constructor:
connect(server_, &Server::RecieveMessage, this, &ChessLanTEst::ShowMessage);
but I don't receive this signal and I don't know why.
Based on comments, you emit in constructor of server_, before you do connect (because constructor has returned if you have valid value in server_ pointer).
Emits themselves are not queued ever, connect must have been done before emit for the slot to be called (or the call queued).
You need to use the SIGNAL and SLOT macros. Like:
connect(server_, SIGNAL(RecieveMessage(QString)), this, SLOT(ShowMessage(QString)));
If you search for the definitions of these macro's, you'll find that the arguments are actually converted to strings.