Results 1 to 16 of 16

Thread: Best approach with model/view implementation

  1. #1
    Join Date
    Jun 2008
    Location
    Glenwood, NJ USA
    Posts
    32
    Thanks
    1
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Best approach with model/view implementation

    I am becoming frustrated as I cannot seem to come up with a good way to implement the required functions in my custom data structure to adhere to the specifications of the QAbstractItemModel. I designed this program originally as a console program and it is eventually going to become a network backup simulator for performance analysis. My custom data structure is an undirected acyclic graph of nodes which are the (networks/LANs) which have children (clients/servers) that also have children (NIC cards, storage volumes). They are all Data Movers and are based on the BUSDataMover class. I use polymorphism to make the objects functional.

    Most of the examples use a central repository for data that is typically a QVector or QList of QVariants. Because of the polymorphic behavior and private members at different levels in the object hierarchy, I have tried a number of different ways to implement this and can't come up with anything that makes sense.

    Does anyone have any experience with this that might lend some advice?

    Thanks.
    Andy

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,363
    Thanks
    3
    Thanked 5,012 Times in 4,791 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Best approach with model/view implementation

    Could you provide some code or at least state what are you having problems with?

  3. #3
    Join Date
    Jun 2008
    Location
    Glenwood, NJ USA
    Posts
    32
    Thanks
    1
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Best approach with model/view implementation

    Here is the code thus far. The implementation of the model functions are in datamovers.h and datamovers.cpp. The model is busmodel.h and busmodel.cpp. I hope it is readable by others... My coding style has never been critiqued since I do this for fun. I would welcome it.
    Attached Files Attached Files

  4. #4
    Join Date
    Jun 2008
    Location
    Glenwood, NJ USA
    Posts
    32
    Thanks
    1
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Best approach with model/view implementation

    I went back and found the problem. I also made the columnCount(), setData(), and data() virtual along with others requiring polymorphic behaviour. Eventually I will eliminate duplicate data in my application but for now I am using a QVector<QVariant> as my model data staging area. I found the problem to be what I mention above not being virtual along with an oversight on what order objects are constructed. I needed to properly size the QVector<QVariant> in the base class before attempting to use the index operator for QVector to insert columns at specified locations in the base class. I still welcome feedback from more experienced coders...

    Thanks.

  5. #5
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,363
    Thanks
    3
    Thanked 5,012 Times in 4,791 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Best approach with model/view implementation

    Method once declared virtual is always virtual, so you don't need to redeclare methods that are virtual in base classes as virtual in subclasses.

  6. #6
    Join Date
    Jun 2008
    Location
    Glenwood, NJ USA
    Posts
    32
    Thanks
    1
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Best approach with model/view implementation

    I'm sorry Wysota, I should have been more clear about what functions I made virtual. I called a number of the functions in my custom base class by the same name. So for example the setData and data functions for the model call setData and data functions in my custom data base class.

  7. #7
    Join Date
    Jun 2008
    Location
    Glenwood, NJ USA
    Posts
    32
    Thanks
    1
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Best approach with model/view implementation

    OK. I have made progress. I can successfully load my data structure into the model. I needed to implement a couple more virtual functions for QAbstractItemModel like headerData and setHeaderData.

    So now I am starting to implement the insertRows() function. It appears that if you start with a beginInsertRows(), execute insertRows(), and then call endInsertRows(), behind the scenes providing that the row/column functionality is correct in the custom data structure, a QModelIndex will be created and the internalPointer will be set by the model. That's fine. But I have a problem. My custom data structure does a number of data validity checks and will not currently allow an empty or default object be created. It seems that I do not have a choice but to further modify my code to add default constructer? Am I correct?

  8. #8
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,363
    Thanks
    3
    Thanked 5,012 Times in 4,791 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Best approach with model/view implementation

    Quote Originally Posted by awhite1159 View Post
    So now I am starting to implement the insertRows() function. It appears that if you start with a beginInsertRows(), execute insertRows(), and then call endInsertRows(),
    No no no, this is wrong. Your implementation of insertRows() should be calling beginInsertRows() and endInsertRows(), not the external code. Besides those methods are protected, so that wouldn't even be possible.

    My custom data structure does a number of data validity checks and will not currently allow an empty or default object be created. It seems that I do not have a choice but to further modify my code to add default constructer? Am I correct?
    Please explain. If you can't have an "empty" object, then don't reimplement insertRows() as it will be useless for you. Instead implement your own method for inserting rows with data. Or make it possible to insert an "empty" object into the model

  9. #9
    Join Date
    Jun 2008
    Location
    Glenwood, NJ USA
    Posts
    32
    Thanks
    1
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Best approach with model/view implementation

    I understand that I cannot execute the beginInsertRows() and endInsertRows() since they are not public but instead protected. It would make sense for me to allow inserting empty objects and if I need to, I will remove the built-in data validity checking that my classes currently have. Right now, as an example, my Net class stores an IP address, Netmask, and it checks to make sure it is not a duplicate IP or an invalid IP. I guess I would need to either handle an exception or move the validity checking code out into Qt and utilize its classes like QRegExp...

    The other approach that you mention which I have also considered but do not know enough about how Qt creates its QModelIndexes is to not use insertRows() and provide my own function. Here is how I think it might work. Below is a code snippet that will help me explain:

    Qt Code:
    1. bool BUSModel::insertRows(int row, int count, const QModelIndex& parent) {
    2. beginInsertRows(parent, row, count);
    3.  
    4. BUSDataMoverItem tmpnet10('L', "NetworkTWELVE", "192.168.12.0", "255.255.255.0", 310000000);
    5.  
    6. endInsertRows();
    7. return true;
    8. }
    To copy to clipboard, switch view to plain text mode 

    Above, I pass the parent, the row (I use rowCount() to append to end of child list.), and the count as 1. I start the beginInsertRows(), add the row to my custom data structure, and then execute an endInsertRows(). The BUSDataMoverItem is a temporary handle that I use to create pointers to BUSDataMover objects. I strip the handle off and use a BUSDataMover* in my child list for each object. Above pass the root item of the tree as the parent and do a append to its child list. Here is the question. Does the model look at the row I am inserting, create an internalPointer to this appended child, create a model index, and update the view? The only thing I have not done is 'emit dataChanged()'

    Do I need to somehow utilize the createIndex() function in my custom insert function? Could the problem be that I really don't have a container where my objects live, I only track them by pointers to the free-store (new/delete).

    This is my base class for the custom data structure and how I establish the hierarcy:
    Qt Code:
    1. //=========================== BUSDataMover Abstract Class ================================
    2. // The base BUSDataMover class
    3. class BUSDataMover {
    4. friend class BUSNetworkSupervisor;
    5. friend class BUSDataMoverItem;
    6. friend ostream& operator<<(ostream&, const BUSDataMover*);
    7.  
    8. public:
    9. BUSDataMover(const string& n, const string& i, BUSDataMover* parent = 0);
    10. BUSDataMover() : mName(""), mIsA(""), qParent(0) {}
    11. virtual ~BUSDataMover();
    12. const string& getName() const { return mName; }
    13. void setName(string& name) { mName = name; }
    14. const int getThruput() { return mThruput; }
    15. virtual void setThruput(int tpkb);
    16. virtual BUSNetId& getNet(int n=0) { }
    17. virtual const string& getIpString(int n=0) { }
    18. virtual const string& getNetmaskString(int n=0) { }
    19. virtual void setIp(const string&, int nn=0) { }
    20. virtual void setnm(const string&, int nn=0) { }
    21. virtual BUSDataMover* addStgV(int, int) { }
    22. virtual BUSDataMover* addNIC(const string&, const string&, int tpkb=0) { }
    23. virtual void handleBUSNetworkMessage(BUSNetworkMessage& m);
    24. virtual map<BUSNetId, BUSDataMover*>& getNodeList() { }
    25. virtual int findCost(const BUSNetId&) { }
    26. virtual void setIn(BUSDataMover* i) { mIn=i; }
    27. virtual void setOut(BUSDataMover* o) { mOut=o; }
    28. BUSDataMover* getIn() { return mIn; }
    29. BUSDataMover* getOut() { return mOut; }
    30. void setIsA(string& isa) { mIsA=isa; }
    31. string& getIsA() { return mIsA; }
    32. virtual map<BUSNetId, BUSDataMover*>::iterator findNode(const BUSNetId&) { }
    33. virtual void addNode(const BUSNetId&, BUSDataMover*, int cost=0) { }
    34. virtual map<BUSNetId, BUSDataMover*>::iterator nodeListEnd() { }
    35. virtual map<BUSNetId, BUSDataMover*>::iterator nodeListBegin() { }
    36. protected:
    37. virtual void print(ostream& o) const;
    38. //============================== Qt members ======================================
    39. public:
    40. BUSDataMover* parent() { return qParent; }
    41. void setParent(BUSDataMover* p) { qParent=p; }
    42. void addChild(BUSDataMover* c) { qChildItems.append(c); }
    43. virtual int columnCount();
    44. QVariant data(int column);
    45. virtual BUSDataMover* child(int row);
    46. bool setData(int column, QVariant data);
    47. virtual int row();
    48. int childCount() { return qChildItems.count(); }
    49. int getIconType() { return qIcon; }
    50. protected:
    51. // The destruction process will utilize this to ensure killing all children.
    52. QList<BUSDataMover*> qChildItems;
    53. BUSDataMover* qParent;
    54. // Icon
    55. int qIcon;
    56. //============================ End Qt members ====================================
    57. private:
    58. string mName;
    59. int mThruput;
    60. string mIsA;
    61. BUSDataMover* mIn;
    62. BUSDataMover* mOut;
    63. };
    To copy to clipboard, switch view to plain text mode 
    Last edited by awhite1159; 3rd July 2008 at 08:43.

  10. #10
    Join Date
    Jun 2008
    Location
    Glenwood, NJ USA
    Posts
    32
    Thanks
    1
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Best approach with model/view implementation

    I removed the beginInsertRows() and endInsertRows. I replaced it with an 'emit layoutChanged()' and now it seems to work. Is thier any downside to doing it this way? The insertRows() function I posted above was for test purposes only. Now that I can insert a row, would it be best to put a generic or blank record in and then go back with a setData() or just put the valid data in immediately? I'll be using a dialog to enter the new row.

  11. #11
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,363
    Thanks
    3
    Thanked 5,012 Times in 4,791 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Best approach with model/view implementation

    Quote Originally Posted by awhite1159 View Post
    I removed the beginInsertRows() and endInsertRows. I replaced it with an 'emit layoutChanged()' and now it seems to work. Is thier any downside to doing it this way?
    Yes, the whole model needs to be reread by the view.

    would it be best to put a generic or blank record in and then go back with a setData() or just put the valid data in immediately? I'll be using a dialog to enter the new row.
    Put the valid data immediately, for instance like this:

    Qt Code:
    1. void myModel::appendNewRow(.... someData){
    2. beginInsertRows(QModelIndex(), rowCount(), rowCount());
    3. MyStruct struct = someData;
    4. m_list << struct; // m_list is the container the model operates on
    5. endInsertRows();
    6. }
    To copy to clipboard, switch view to plain text mode 

  12. #12
    Join Date
    Jun 2008
    Location
    Glenwood, NJ USA
    Posts
    32
    Thanks
    1
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Best approach with model/view implementation

    Thank you Wysota.

    The only thing I need to be clear on is whether or not the model requires the custom data be in a central repository or container. With the current code since I am still in the process of learning Qt and how it works, I chose to populate the data prior to instantiating the model. I noticed while tracing there are no calls to setData(). All the calls are to the models data() function. So this would imply to me that the current implementation of my model interface to my custom data only works correctly for existing data in the custom data structure as a whole. Hence, that is why the layoutChanged() works and beginInsertRows/endInsertRows does not.

    My model is not aware of a central repository. I simply provide pointers during the intitalization of the model as to where the data resides on the heap via the parent/child members of my base class.

  13. #13
    Join Date
    Jun 2008
    Location
    Glenwood, NJ USA
    Posts
    32
    Thanks
    1
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Best approach with model/view implementation

    I know that you assume that I have a good working knowledge of C++ and I believe I do. But it is always a learning process. I am no where being an expert as I only do this in my recreation. It seems that a large number of the problems are not with Qt but instead with the poster's knowledge of C++. I am probably one of those and I thank you for supporting me.

    I have no copy constructor for my custom base class. I utilize the default implicit copy constructor. Could that be the problem? Does the model need more than a shallow copy of the custom data structure?

  14. #14
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,363
    Thanks
    3
    Thanked 5,012 Times in 4,791 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Best approach with model/view implementation

    Quote Originally Posted by awhite1159 View Post
    The only thing I need to be clear on is whether or not the model requires the custom data be in a central repository or container.
    No, it doesn't. There can even be no real data at all, it can all be generated on the fly (for example the multiplication table).

    I chose to populate the data prior to instantiating the model.
    This is fine as long as the data doesn't change behind the model's back.

    Hence, that is why the layoutChanged() works and beginInsertRows/endInsertRows does not.
    No, this is wrong. beginInsertRows()/endInsertRows() doesn't modify the data in any way, it just informs the views that rows are being added in a particular place so that they know they might need to update their viewports.

    I simply provide pointers during the intitalization of the model as to where the data resides on the heap via the parent/child members of my base class.
    This is fine.

    Quote Originally Posted by awhite1159 View Post
    I have no copy constructor for my custom base class. I utilize the default implicit copy constructor. Could that be the problem?
    No, as long as you know what you are doing with the pointers inside the structure.

    Does the model need more than a shallow copy of the custom data structure?
    The model doesn't need anything. All access to the data is performed through methods you implement, so you have total control over what is going on.

  15. #15
    Join Date
    Jun 2008
    Location
    Glenwood, NJ USA
    Posts
    32
    Thanks
    1
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Best approach with model/view implementation

    Thanks Wysota.

    I understand your responses and thank you.

    Well, I am completely at a loss and there must be a problem with how I have implemented this. I will examine all my code and report back on my findings.

  16. #16
    Join Date
    Jun 2008
    Location
    Glenwood, NJ USA
    Posts
    32
    Thanks
    1
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Best approach with model/view implementation

    Silly problem corrected. My insertRows() function had parameters which passed the starting row, the count, and the parent. I was using the value for the count in the beginInsertRows() as it's last parameter. So the call to beginInsertRows() ended up being invalid:

    beginInsertRows(startingrow, count, QModelIndex());

    This would not work since it had a negative range since the actual call I was making had the strting row as 10 and the ending row as 1 when I attempted to insert 1 row at the end of the list.

    Thank you Wysota for all your help.

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.