Results 1 to 20 of 54

Thread: Integration: Nested Tree Parent Child C++ Classes with Models/Delegates for QML

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Integration: Nested Tree Parent Child C++ Classes with Models/Delegates for QML

    I don't see anything that would narrow your option down to just (1), however, it is often benefitial to not design data structures and data handling with just the UI's needs in mind.

    Lets look at this with a simplied example, an address book:

    1) for that I would have
    * a Contact class that can hold all data for a single address book entry
    * an AddressBook class that has a list or maybe even a hash of Contact objects and which would have methods to add/remove/change/get/list contacts

    2a) a list model that gets a pointer to an AddressBook and provides the list of contacts, contact details as roles

    2b) an Import/Export class or classes that also get an AddressBook pointer or reference and implement data I/O

    3) optionally, a Contact adapter/editor class that serves as an QML adapter for modifying a single contact. It would get a Contact object and provide its data as separate Q_PROPERTY fields, it can provide methods to check for validity of the data, etc.

    (2a) and (2b) are different usages of the data but can still work on the same data. the base classes only need to ensure they can provide all the functionality required for these two use cases.

    For your application I wonder if you really want cards nested inside albums.
    I.e. is an Album not just a "view" of cards, a selection from the whole?
    For example could a user have "All my cards", "All time favorites", "Favorite Water Pokemons", with the same "Vaporeon" card being in all three?

    Cheers,
    _

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

    Nizars (30th December 2016)

  3. #2
    Join Date
    Dec 2016
    Posts
    46
    Thanks
    20
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Integration: Nested Tree Parent Child C++ Classes with Models/Delegates for QML

    You make a good point!
    I didn't really think of that. The thing is, for now at least and to narrow down my project, I have decided to work only with one set of Pokemon cards. Those cards exist in <cardID>.json files in a resource. Those JSON documents contain all the variable values for the cards such name, hp, attacks and so on. The card images exist in another folder in the resources as <cardID>.jpg. I have done this so that I have something to work with and not risk spending a month or two working with network, ssl, cache, json classes and risk not having anything to present as the deadline aproaches. I do like the project I am working with and I am planning to add those functionalities in the future, maybe even a function that adds cards by scanning them with a phone camera. The JSON documents are retreived from: pokemontcg.io but that is something that I won't touch until the main application fully works.

    My line of thinking has been that when the user wants to add a card to an album. He or she will type the name or the id of the card and the program creates an instance of that card in the album. If he doesn't add it to the album and decided just to see it, it will be deleted once he exits the view. I also thought of having an instance of all the cards pre-loaded in a hidden default album that the App uses to display the cards before the user adds them to his or her Album.

    You make a good point about a Card being available in more than one album. I guess there should be a default "All my cards" Album. I guess cards in that Album determines what cards are made available to the other albums. I assume that this would be a bit problematic if the user wants to create a "My wishlist" or "To buy" Album. There is also the problem of duplicate cards.

    I will try to rethink the relationships between the Albums and between the Cards and Albums. I think that having an Album as an isolated container of cards from other Cards in other Albums might make the implementation a bit easier assuming that the user will manually add Cards to Albums and not expect them to be shared among the Albums. I can however create a "view" that shows all the cards in all the albums or multiple Albums.

  4. #3
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Integration: Nested Tree Parent Child C++ Classes with Models/Delegates for QML

    Just sketching some ideas here:

    Qt Code:
    1. class Card
    2. {
    3. QString id;
    4. QString name;
    5. // ....
    6. };
    7.  
    8. class CardManager : public QObject
    9. {
    10. Q_OBJECT
    11.  
    12. public:
    13. // returns an invalid card if ID is not found, e.g. Card object with empty "id"
    14. Card addCardById(const QString &id);
    15. Card addCardByName(const QString &name);
    16.  
    17. Card card(const QString &id) const;
    18. QList<Card> cards() const;
    19.  
    20. int count() const;
    21.  
    22. signals:
    23. void cardAdded(const QString &id);
    24. void cardRemoved(const QString &id);
    25.  
    26. private:
    27. QList<Card> m_cards;
    28. };
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. class Album
    2. {
    3. QString id;
    4. QString name;
    5. QStringList cardIDs;
    6. };
    7.  
    8. class AlbumManager : public QObject
    9. {
    10. Q_OBJECT
    11. public:
    12. Album createAlbum(const QString &name);
    13.  
    14. Album album(const QString &id) const;
    15.  
    16. QList<Album> albums() const;
    17.  
    18. int count() const;
    19.  
    20. void addCard(const QString &albumId, const QString &cardId);
    21. void removeCard(const QString &albumId, const QString &cardId);
    22.  
    23. signals:
    24. void albumAdded(const QString &id);
    25. void albumRemoved(const QString &id);
    26.  
    27. void cardAdded(const QString &albumId, const QStringList &cardIDs);
    28. void cardRemoved(const QString &albumId, const QStringList &cardIDs);
    29. };
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. class AbstractCardModel : public QAbstractListModel
    2. {
    3. Q_OBJECT
    4. public:
    5. enum Roles {
    6. IdRole = Qt::UserRole + 1,
    7. NameRole,
    8. // ....
    9. }
    10.  
    11. explicit AbstractCardModel(CardManager *cardManager, QObject *parent = 0);
    12.  
    13. // this uses cardForIndex to get the card, then uses role to return the actual data
    14. QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    15.  
    16. protected:
    17. QHash<int, QByteArray> roleNames() const;
    18.  
    19. virtual Card cardForIndex(const QModelIndex &index) const = 0;
    20.  
    21. protected:
    22. CardManager *m_cardManager;
    23. };
    To copy to clipboard, switch view to plain text mode 
    Qt Code:
    1. // for showing the cards of a single album
    2. class AlbumCardsModel : public AbstractCardsModel
    3. {
    4. Q_OBJECT
    5. public:
    6. AlbumCardsModel(AlbumManager *albumManager, CardManager *cardManager, QObject *parent = 0);
    7.  
    8. void showAlbum(const QString &albumId);
    9.  
    10. int rowCount(const QModelIndex &parent) const;
    11.  
    12. protected:
    13. Card cardForIndex(const QModelIndex &index) const;
    14.  
    15. private slots:
    16. // gets connected to m_albumManager's albumRemoved() signal
    17. void onAlbumRemoved(const QString &albumId);
    18.  
    19. void onCardAdded(const QString &albumId, const QString cardId);
    20. void onCardRemoved(const QString &albumId, const QString cardId);
    21.  
    22. private:
    23. AlbumManager *m_albumManager;
    24. Album m_album;
    25. };
    26.  
    27. void AlbumCardsModel::showAlbum(const QString &albumId)
    28. {
    29. beginResetModel();
    30.  
    31. m_album = m_albumManager->album(albumId);
    32.  
    33. endResetModel();
    34. }
    35.  
    36. int AlbumCardsModel::rowCount(const QModelIndex&) const
    37. {
    38. return m_album.cardIDs.count();
    39. }
    40.  
    41. Card AlbumCardsModel::cardForIndex(const QModelIndex &index) const
    42. {
    43. return m_cardManager->card(m_album.cardIDs.at(index.row());
    44. }
    45.  
    46. void AlbumCardsModel::onAlbumRemoved(const QString &albumId)
    47. {
    48. if (albumId != m_album.id) return;
    49.  
    50. beginResetModel();
    51. m_album = Album();
    52. endResetModel();
    53. }
    54.  
    55. void AlbumCardsModel::onCardAdded(const QString &albumId, const QString &cardId)
    56. {
    57. if (albumId != m_album.id) return;
    58.  
    59. const Album updatedAlbum = m_albumManager->album(albumId);
    60. const int row = updatedAlbum.cardIDs.indexOf(cardId);
    61.  
    62. beginInsertRows(QModelIndex(), row, row);
    63.  
    64. m_album = updatedAlbum;
    65.  
    66. endInsertRows();
    67. }
    68.  
    69. void AlbumCardsModel::onCardRemoved(const QString &albumId, const QString &cardId)
    70. {
    71. if (albumId != m_album.id) return;
    72.  
    73. const Album updatedAlbum = m_albumManager->album(albumId);
    74. const int row = m_album.cardIDs.indexOf(cardId);
    75.  
    76. beginRemoveRows(QModelIndex(), row, row);
    77.  
    78. m_album = updatedAlbum;
    79.  
    80. endRemoveRows();
    81. }
    To copy to clipboard, switch view to plain text mode 

    Cheers,
    _

  5. The following 2 users say thank you to anda_skoa for this useful post:

    d_stranz (30th December 2016), Nizars (30th December 2016)

  6. #4
    Join Date
    Dec 2016
    Posts
    46
    Thanks
    20
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Integration: Nested Tree Parent Child C++ Classes with Models/Delegates for QML

    I really appreciate your time and all the help you are offering me on this!

    I have a quick question for you while I finish making changes to my classes.

    What is the role of a <class>Manager class? I am just trying to understand how it fits in.
    Say I want to to add copy constractors and assignment opperators for the App, Album and Card classes, where would those be implemented? in their respective classes? or in the their accompanying <class>Manager classes?
    What about the Setter and Getter class methods such as "QString getCardName() const;"? Same with the default and overloaded constructors? should I even have a default constructor in the original class?

    I have noticed that you combine each class and it's Manager class in the same document. Is there a reason or a benifit behind this practise? or is this simple down to preference and time saving?
    I have created three different classes for each of the "core" classes. An Album class, an AlbumsManager class and an AlbumModel class. I am totally open to changing it to the template you wrote if it makes a difference.

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

    Default Re: Integration: Nested Tree Parent Child C++ Classes with Models/Delegates for QML

    Yes, anda_skoa has been very generous with his help on this one and has given you a well-thought-out and flexible design.

    If you use the Card and Album classes as anda_skoa has defined them in his code, you don't actually need an explicit copy constructor (or even an assignment operator) because C++ will implement default ones for you. Usually default constructor / assignment does a bitwise copy of the right-hand side instance (i.e. the instance you are copying from), except when the class being copied contains member variables which themselves have copy / assignment operators, which QString and QStringList do (and which fundamental types like int, float, etc. also have). If you add anything to the definitions of these classes which does not have explicit copy / assignment, you'll either have to make copy and assignment methods for them, or implement them in the Card or Album class. For example, if you add a pointer variable, you'll probably want to either wrap it in a smart pointer (which has copy / assignment), make a deep copy of the contents of the instance pointed to, or add another "manager" which is in charge of the lifetime of these pointers (rather than delete them in the Card or Album destructor).

    Combining the Card and CardManager class in the same file is basically just for convenience. More importantly, only the CardManager should be allowed to create Card instances, because then it will automatically add them to the list of Card instances it is managing. Likewise for Album and AlbumManager. You can't have a standalone, unmanaged Card or Album. But on the other hand, anda_skoa's design allows for cards that belong to no album as well as cards that belong to multiple albums.

    You'll probably only have one instance each of the AlbumManager and CardManager classes, but the design also anticipates that you might want to expand to handle multiple users, each one of whom will have their own card and album collections, so you'd need a card and album manager for each of them.

    The AlbumManager and CardManager classes are the GUI-independent classes that are used by the AlbumCardsModel. This model is the interface to the views; when either the CardManager or AlbumManager make changes to their content, they emit signals that the model listens for so it can in turn update the views. You might have several instances of AlbumCardsModel - one for interfacing to one or more views of different albums.
    Last edited by d_stranz; 31st December 2016 at 01:02.
    <=== 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. The following user says thank you to d_stranz for this useful post:

    Nizars (31st December 2016)

Similar Threads

  1. Nested Models
    By Dan7 in forum Qt Programming
    Replies: 1
    Last Post: 26th August 2015, 20:31
  2. Models and Delegates
    By bgeller in forum Newbie
    Replies: 13
    Last Post: 4th March 2010, 04:46
  3. QStandardItemModel, parent / child to form tree structure
    By Nightfox in forum Qt Programming
    Replies: 2
    Last Post: 8th January 2010, 17:01
  4. Nested delegates in a QListWidget
    By youkai in forum Qt Programming
    Replies: 9
    Last Post: 7th April 2009, 08:48
  5. Models, delegates or views and how?
    By maddogg in forum Newbie
    Replies: 3
    Last Post: 9th November 2007, 13:59

Tags for this Thread

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.