Solved How to connect signal slots between many objects
-
What kind of messages are you going to exchange between these various unrelated objects ?
-
@SGaist
I'm very sorry, I don’t quite understand what "What kind of messages" means~
@Asperamanca’s solution explains my doubts very well. If there is no other solution, I will follow Asperamanca ’s suggestion.
Best regards! -
@Asperamanca Hi, thank you very much!
Your solution is the answer I want, but my English is too bad, and I can't always explain my problem.
Take the communication between child1 and child2 as an example, is the code like this?CentralModel
class CentralModel : public QObject, public Singleton { Q_OBJECT public: explicit CentralModel(QObject *parent = nullptr) : QObject(parent) {} ~CentralModel() {} private: QVariant m_data; //!< shared data between child1 and child2 QReadWriteLock m_dataLock; //!< lock for m_data public: QVariant data() //!< get data { QReadLocker locker(&m_dataLock); return m_data; } void setData(const QVariant value) //!< set data { QWriteLocker locker(&m_dataLock); if (m_data != value) { m_data = value; emit dataChanged(value); } } signals: void dataChanged(const QVariant value); };
Child1
class Child1 : public QObject, { Q_OBJECT public: explicit Child1(QObject *parent = nullptr) : QObject(parent) {} ~Child1() {} public: QVariant func() { CentralModel::getInstance()->setData(3.14); } };
Child2
class Child2 : public QObject, { Q_OBJECT public: explicit Child2(QObject *parent = nullptr) : QObject(parent) { QObject::connect(CentralModel::getInstance(), SIGNAL(dataChanged(const QVariant)), SLOT(onDataChanged(const QVariant))); } ~Child2() {} private slots: void onDataChanged(const QVariant value) { qDebug() << value.toDouble(); } };
-
I would only design the central model as a singleton if you are sure you'll not need a separate model in the future (e.g. old project state vs current project state).
You sample code doesn't tell me what you mean by "QVariant data()". If it was just an example, and in reality you'll have multiple properties that should work fine. What I would not do is put every information you have inside the data() and notify everyone if anything changes.One nice trick (if you need it): If you store the data of the whole data model in a separate, copyable class (just place an instance in the DataModel QObject class and use it there), you can always create snapshots of older states of your project data. Also, you can easily implement transaction logic (multiple changes are only applied together once you are ready to push them).
-
@Asperamanca
Yes, "QVariant data()" is just an example, there are many different types of data and signals in actual projects. -
@Asperamanca said in How to connect signal slots between many objects:
One nice trick (if you need it): If you store the data of the whole data model in a separate, copyable class (just place an instance in the DataModel QObject class and use it there), you can always create snapshots of older states of your project data. Also, you can easily implement transaction logic (multiple changes are only applied together once you are ready to push them).
Do you mean like this?
class ExampleModel1 : public QObject //!< Copyable { Q_OBJECT public: explicit ExampleModel1(QObject *parent = nullptr); ~ExampleModel1(); private: QVariant exampleData1; public: QVariant getExampleData1(); void setExampleData1(const QVariant value); signals: void exampleData1Changed(const QVariant value); }; class ExampleModel2 : public QObject //!< Copyable { Q_OBJECT public: explicit ExampleModel2(QObject *parent = nullptr); ~ExampleModel2(); private: QVariant exampleData2; public: QVariant getExampleData2(); void setExampleData2(const QVariant value); signals: void exampleData2Changed(const QVariant value); }; class CentralModel : public QObject, public Singleton //!< Singleton, Not copyable { Q_OBJECT public: explicit CentralModel(QObject *parent = nullptr); ~CentralModel(); public: ExampleModel1 *model1; QReadWriteLock lock1; // lock for model1 ExampleModel2 *model2; QReadWriteLock lock2; // lock for model2 };
-
@tovax
QObject is never copyable. So the data has to be in classes not derived from QObject. If your only access to these data classes is via you (QObject-derived) Data model, then you have full control when data is changed, and the data model can reliably fire notification signals. -
@tovax said in How to connect signal slots between many objects:
@SGaist
I'm very sorry, I don’t quite understand what "What kind of messages" means~I meant what kind of data are you going to pass around ? Depending on that you might want to consider a publish/subscribe system like mqtt and the corresponding QtMQTT module.
-
@Asperamanca
I will try to implement the central model in the project based on your suggestions. Maybe my understanding is not enough at present, but I think I will have a deeper understanding in the process of doing it.
Best regards!class CentralData { public: CentralData(); ~CentralData(); QVariant exampleData1; QVariant exampleData2; QVariant exampleDataN; }; class CentralModel : public QObject { public: CentralModel(); ~CentralModel(); private: CentralData centralData; public: QVariant getExampleData1(); void setExampleData1(const QVariant value); QVariant getExampleData2(); void setExampleData2(const QVariant value); QVariant getExampleDataN(); void setExampleDataN(const QVariant value); signals: void exampleData1Changed(const QVariant value); void exampleData2Changed(const QVariant value); void exampleDataNChanged(const QVariant value); };
-
@SGaist Hi, thank you very much for your patience.
Data types supported by QVariant, and some custom structures. I will carefully read the link you gave. -
@tovax said in How to connect signal slots between many objects:
@Asperamanca
I will try to implement the central model in the project based on your suggestions. [...]Yes, that looks about like what I was suggesting.
A few notes:- If you ever set the centralData (e.g. to revert to an old state), you'll need a mechanism to trigger all change signals that apply
- If all your data is QVariant, you could store it in a single container, and access it via Enum:
QVariant getData(const EDataKey eKey) const;
However, signals are tricky in this case. You could have a single signal that passes the changed EDataKey (or a List or Set of changed keys for a sum change notification), but it would mean that everyone who is interested in data change will get all notifications, and needs to discard those that are not relevant. You could still make single signals and only one setData/getData, but this would mean some ugly switches in your code, and an inconsistent interface.
But this brings me to another question:
- Why make all data QVariant? You lose the benefits of type safety. What do you gain?
-
@Asperamanca Hi,
I have two kinds of data, the first is the parameter obtained from the database, and its type is QVariant, which is converted when it is used; the second is my customized structure data.