Results 1 to 9 of 9

Thread: Tree model implementation for C++ data to QML

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Tree model implementation for C++ data to QML

    I take the name literally: QAbstractItemModel: it is an abstraction of some underlying data structure that acts as a well-defined interface between that data structure and a visualization of that data structure in a user interface. The view interacts with the model, the model interacts with the data structure. There is no need to rewrite the view when the data structure changes because the model-view interface doesn't change.

    I've used hash tables / maps, linked lists, vectors, a Tree class where the actual data for each node is stored in a void pointer data member and all the tree structure does is provide the parent-child links for navigation. The Tree class might work well for your problem, since all you have is a simple hierarchy.

    Each Tree node could contain a "node type" enum (ClassroomType, GroupType, PersonType) which would tell you how to cast the void pointer to an actual class instance pointer. When your underlying data changes, you simply destroy the whole tree and rebuild it (bracketed by calls to beginResetModel() and endResetModel()). The pointer to the Tree node is stored in the QModelIndex's internal data pointer.

    Qt Code:
    1. struct Tree
    2. {
    3. Tree * parent = nullptr;
    4. QList< Tree * > children;
    5. int nodeType = -1;
    6. void * nodeData = nullptr;
    7.  
    8. ~Tree()
    9. {
    10. for ( child : children)
    11. delete child;
    12. }
    13. };
    14.  
    15. QModelIndex MyModel::index( int row, int column, const QModelIndex & parent ) const
    16. {
    17. QModelIndex newIndex;
    18. if ( parent.isValid() )
    19. {
    20. if ( row >= 0 && row < rowCount( parent ) && column >= 0 && column < columnCount( parent ) )
    21. {
    22. Tree * parentNode = static_cast< Tree * >( parent.internalPointer() );
    23. Tree * childNode = parentNode->children[ row ];
    24. newIndex = createIndex( row, column, (void *)childNode );
    25. }
    26. }
    27. return newIndex;
    28. }
    29.  
    30. QVariant MyModel::data( const QModelIndex & index, int role ) const
    31. {
    32. QVariant returnVal;
    33. if ( index.isValid() && role == Qt::DisplayRole )
    34. {
    35. Tree * node = static_cast< Tree * >( index.internalPointer() );
    36. switch( node->nodeType )
    37. {
    38. case ClassroomType:
    39. returnVal = "Classroom";
    40. break;
    41.  
    42. case GroupType:
    43. {
    44. Group * group = static_cast< Group * >(node->nodeData);
    45. returnVal = group->name;
    46. }
    47. break;
    48.  
    49. // etc.
    50. }
    51. }
    52. return returnVal;
    53. }
    To copy to clipboard, switch view to plain text mode 

    If the underlying data structure has multiple "columns", then the data() method will have to map from a column number to a field in the data structure; for example column 0 of a Group node would be the group name, column 1 the location. You just need to expand each clause of the switch statement to retrieve the column data.
    Last edited by d_stranz; 24th January 2021 at 00:55. Reason: updated contents
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  2. The following user says thank you to d_stranz for this useful post:

    xconverge (24th January 2021)

  3. #2
    Join Date
    Jan 2021
    Posts
    5
    Thanks
    3

    Default Re: Tree model implementation for C++ data to QML

    I hope that I don't end up hitting a wall with the need to beginResetModel/endResetModel to update the entire model and thus probably the entire view if just a single element in a child of a child is changed, I don't think this will be an issue for my application (5-10 persons per 1-5 groups, per 1 classroom), but it is worth thinking about. Just because I am new to QML (not new to Qt necessarily), you allude to the shortcut/less maintainable version that piqued my interest, that does not adhere/implement the abstract interface. Would that be a QObject with properties and invokable functions? If I were to go that route, I would get a change notification per property potentially, and be more performant than the original suggestion, wouldn't I? The downside as you mentioned is maintainability of the interface between the model and the view.

    The view is basically going to be a classroom view, with a tabview of groups, and in each tab a rowlayout of persons

    Thanks for your replies, I really appreciate it!

  4. #3
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Tree model implementation for C++ data to QML

    I don't think this will be an issue for my application (5-10 persons per 1-5 groups, per 1 classroom)
    No. I use a similar structure for a tree with one root element, 5 top level elements under that, and 10 - 15 children under each of those. Rebuilding the tree and re-displaying it when something changes is instantaneous.

    I don't see any advantage in using QObject as a base class for anything in the data structure; if you did, you'd have to change it from QList< whatever > to QList< whatever * > since QObject doesn't allow copy or assignment semantics.

    I am a big believer in clean data structures that are not encumbered by a particular framework (like Qt), so I tend to lean towards straight C++ / STL to represent them. If I need to have an editable data structure, then I will devise an editor class that -is- Qt-based so that I can have that class manage the changes to the data structure and send signals to the rest of the UI to make updates.

    I help maintain a large library of data and algorithm classes that is entirely written in vanilla C++ / STL. It is used in both Qt and MFC applications on Windows, as well as in Qt applications on linux, OS/X, and android platforms. It's probably close to a half-million lines of code. So keeping things clean and portable is a big part of what I've learned to do.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  5. The following user says thank you to d_stranz for this useful post:

    xconverge (24th January 2021)

  6. #4
    Join Date
    Jan 2021
    Posts
    5
    Thanks
    3

    Default Re: Tree model implementation for C++ data to QML

    Quote Originally Posted by d_stranz View Post
    No. I use a similar structure for a tree with one root element, 5 top level elements under that, and 10 - 15 children under each of those. Rebuilding the tree and re-displaying it when something changes is instantaneous.

    I don't see any advantage in using QObject as a base class for anything in the data structure; if you did, you'd have to change it from QList< whatever > to QList< whatever * > since QObject doesn't allow copy or assignment semantics.

    I am a big believer in clean data structures that are not encumbered by a particular framework (like Qt), so I tend to lean towards straight C++ / STL to represent them. If I need to have an editable data structure, then I will devise an editor class that -is- Qt-based so that I can have that class manage the changes to the data structure and send signals to the rest of the UI to make updates.

    I help maintain a large library of data and algorithm classes that is entirely written in vanilla C++ / STL. It is used in both Qt and MFC applications on Windows, as well as in Qt applications on linux, OS/X, and android platforms. It's probably close to a half-million lines of code. So keeping things clean and portable is a big part of what I've learned to do.
    That is great empirical info on the performance front, thanks

    I planned on using vanilla/stl for the data structures on the backend elsewhere to avoid pulling in the qt dependencies and was going to write a translation layer into the qobject versions, so removing that extra level of abstraction for no reason is going to be helpful and makes a lot of sense!

    If set up this way, what would the pseudocode of the qml look like to access the first person in the first group of the classroom look like from my ClassroomModel singleton?

    I will take a crack at it with this new mindset/plan and report back, thanks a bunch!

  7. #5
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Tree model implementation for C++ data to QML

    what would the pseudocode of the qml look like
    Well, there's where you've reached the limit of my knowledge. I've never written any serious code using the QML / QtQuick framework. Everything I do is QWidget-based desktop apps.

    Maybe someone else with more experience can jump in.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  8. #6
    Join Date
    Jan 2021
    Posts
    5
    Thanks
    3

    Default Re: Tree model implementation for C++ data to QML

    So I was able to get a prototype working that looks something like this (warning: pretty far from the implementation idea discussed so far). This prototype captures the simplicity of how I "thought" things should be able to work, so at least now I don't feel crazy. I by no means think this is the right or best way though, but that is mentioned at the bottom of this post

    Qt Code:
    1. class Person : public QObject {
    2. Q_OBJECT
    3. Q_PROPERTY(QString name MEMBER m_name)
    4. Q_PROPERTY(int age MEMBER m_age)
    5. public:
    6. explicit Person(Group* parent = nullptr);
    7. explicit Person(const QString& name, int age, Group* parent = nullptr);
    8.  
    9. private:
    10. QString m_name = "";
    11. int m_age;
    12. };
    13.  
    14. class Group : public QObject {
    15. Q_OBJECT
    16. Q_PROPERTY(QString nickname MEMBER m_nickname)
    17. public:
    18. explicit Group(Classroom* parent = nullptr);
    19. explicit Group(QString nickname, Classroom* parent = nullptr);
    20. ~Group();
    21.  
    22. void appendPerson(Person* person);
    23. Q_INVOKABLE QObject* person(int index);
    24. int personCount() const;
    25. signals:
    26. void personCountChanged();
    27.  
    28. private:
    29. QString m_nickname;
    30. QList<Person*> m_people;
    31. };
    32.  
    33. class Classroom: public QObject {
    34. Q_OBJECT
    35. Q_PROPERTY(int groupCount READ groupCount NOTIFY groupCountChanged)
    36. public:
    37. Classroom(QObject* parent = nullptr);
    38. ~Classroom();
    39.  
    40. void appendGroup(Group* group);
    41.  
    42. Q_INVOKABLE QObject* getGroup(int index) const;
    43. int groupCount() const;
    44. signals:
    45. void groupCountChanged();
    46.  
    47. private:
    48. // Temp debug code to populate the model
    49. void populateModel();
    50.  
    51. QList<Group*> m_groups;
    52. };
    To copy to clipboard, switch view to plain text mode 

    and then in QML I am able to access it with stuff like this:

    Qt Code:
    1. console.log(ClassroomModel.getGroup(groupNum).person(1).name);
    To copy to clipboard, switch view to plain text mode 

    or with a property

    Qt Code:
    1. property int groupCount: ClassroomModel.groupCount
    To copy to clipboard, switch view to plain text mode 

    I think I understand the tradeoffs between using this vs subclassing a qabstractmodel, but wanted to put my thoughts on these explicitly incase there is something I am missing

    1) My datastructures are QObjects, not raw clean c++/stl, so a translation layer will be needed if I want to use raw stl structures on the backend to populate the model
    2) I wrote a custom interface, and my view code will need to be updated more often than if I were to have used the interface from qabstractmodel
    3) I can put in notify signals wherever I want really at any layer
    4) Performance as you said doesn't seem to be bad to do a complete destroy/recreate of my view with the number of items I am talking, but there is room here to optimize using some smarter notify/change signals
    5) Not compatible with tableview/etc as is, but can be used for Repeater counts and anything else where an exposed property would suffice
    6) I can even use this https://doc.qt.io/qt-5/qtqml-cppinte...ect-list-types to simplify the API even more

    This looks like it is going to work for my application, but I would love to hear about any counterpoints (either the same as what I identified above, or different/new)

    I was really struggling with modelindex and how it could work for me, but that could always be a refactor/change I tackle in the future.
    Last edited by xconverge; 24th January 2021 at 23:29.

Similar Threads

  1. Replies: 3
    Last Post: 17th January 2016, 18:06
  2. Replies: 3
    Last Post: 16th November 2015, 21:22
  3. How to map tree model data to list view
    By msopanen in forum Qt Programming
    Replies: 0
    Last Post: 10th November 2009, 19:56
  4. Replies: 1
    Last Post: 7th July 2009, 07:13
  5. data, model and tree view
    By larry104 in forum Qt Programming
    Replies: 17
    Last Post: 3rd July 2006, 14:43

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.