Unsolved Qt's common patterns for collection change signals?
-
Hello!
While Qt has signals for notifying other objects about some property changed, it doesn't seem to have something similar for collections. Is there anything available better than just
collectionChanged
signal?In principle it's possible to create QObject-derived container which provides required functionality. Although it would result in QObject-in-QObject, with all the overhead. The other alternative is to manually implement all necessary collection modification methods, which would result in boilerplate code.
Any thoughts or advices?
Thanks
-
@Igor-Baidiuk
Where is thiscollectionChanged
signal? -
Hi,
Not all Qt classes derives from QObject.
Containers are such classes.
It seems you are looking for the model classes.
-
In general, I want to design tree-like hierarchy of objects:
class Node: public QObject { ... // Root type of type hierarchy }; class Assembly: public Node { ... Q_PROPERTY(QVector<Node*> children ...) ... }; class Face: public Node { ... }; class Edge: public Node { ... }; class Vertex: public Node { ... }; class Part: public Node { ... Q_PROPERTY(QVector<Face*> faces ...) Q_PROPERTY(QVector<Edge*> edges ...) Q_PROPERTY(QVector<Vertex*> vertices ...) ... };
in such a way that each object would emit signals about changes in its child sets (not just "children changed", being more specific).
And I would prefer to not copypaste all the code each time I need another collection, since hierarchy would be more complex than one presented above.I mean such case as:
class SomeClass: public QObject { Q_OBJECT Q_PROPERTY(QVector<SomeValueType> collection READ collection WRITE setCollection NOTIFY collectionChanged) public: QVector<SomeValueType> collection() const; void setCollection(QVector<SomeValueType>); // some helper methods void collectionAddElement(SomeValueType); void collectionRemoveElement(int); signals: void collectionChanged(int start, int oldCount, int newCount); };
Versus approach with separate collection wrapper class
class CollectionSignals : public QObject { Q_OBJECT signals: changed(int start, int oldCount, int newCount); }; template<typename T> class Collection: public CollectionSignals { public: int size() const; void add(T value); T remove(int at); T get(int at); QVector<T> asVector() const; // ... other methods }; class SomeClass: public QObject { Q_OBJECT Q_PROPERTY(Collection<SomeValueType>* collection READ collection) public: Collection<SomeValueType>* collection() const { return &_collection; } private: Collection<SomeValueType> _collection; };
First approach seems more lined up with general Qt ideology, although it requires more manual coding.
Second approach removes need for most boilerplate, and gives common functionality for all similar collections, although it makes each such collection into separate QObject (with its overhead) and doesn't seem to fit that well with Qt's intended structuring. I.e. access to list of its values from QML would besomeObject.collection.asVector()
, no simple property binding viaelements: someobject.collection
etc.Sorry, I may've misread you.
QVector
,QList
, evenQQmlListProperty
don't provide signals for specifically their changes. AndQAbstractItemModel
is IMO overkill since it represents recursively nested rectangular tables. -
Use a QAbstractItemModel - it does all what you describe above.
-
@Christian-Ehrlicher As I mentioned above, QAbstractItemModel is too abstract. It requires considerable amount of coding for implementation, and is rather cumbersome as simple collection of values. I'm looking for something that is usable from both UI and C++ logic sides.
-
You might find it overkill but in fact, it provides all what you require including UI and backend usability.
You should also take into account that QObject based classes cannot be copied, therefore you would have to work only with containers of pointers. Also, QObject, while not being a superheavy class would also adds up pretty quickly to the memory cost of your containers.
Keeping your data structure as standard containers and putting a model on top of it will keeps things simple and clean. You can add API's to the model in order to manipulate the underlying data structure and in theses methods you'll be able to properly trigger the various signals you need for your UI to be updated automatically.
-
Am I right you're suggesting something like this:
class MyItemModel: QAbstractItemModel { // ... // implement iterators, simple item getters etc. }; class SomeClass: public QObject { Q_OBJECT Q_PROPERTY(MyItemModel* collection ...) };
-
Your MyItemModel class is not defined enough to be able to give you an answer.
However, like I wrote before, the model sits on top of you data structure, it does not replace it.
-
I know this. What I'm asking is what's the recommended way to implement that underlying collection with change notifications in mind.
-
The collection does not need to have a change notification, the model sends the notifications.
-
@Christian-Ehrlicher So are you suggesting
class SomeClass: public QObject { ... Q_PROPERTY(QAbstractItemModel* collection ...) ... };
?