Results 1 to 5 of 5

Thread: QTreeView - Children Displaying Same Data As Parent, WhatsThisRole is Not

  1. #1
    Join Date
    Jan 2013
    Location
    Wisconsin, USA
    Posts
    3
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Windows

    Question QTreeView - Children Displaying Same Data As Parent, WhatsThisRole is Not

    Hi all,

    First post here - I'm beginning on Qt, having just gotten a good foothold [though I suppose these things are always subjective] on C++. Here's my issue:

    I'm creating a QTreeView hooked up to a custom model that stores file paths for the program to process, sorted by "Device" (or some other arbitrary header). If the program gets passed a valid file path, it should automatically parent it to a new device.

    So, the idea is to have:
    Root Item
    |
    - Device Item #1
    - File #1
    - File #2
    - ....
    - Device Item #2
    [/code]

    In the data structure, everything seems fine. Things are constructed appropriately, in Debug mode, I can walk through from the root item all the way to each child through the recursive layers of QList<Node *>. All the data seems correct.

    When I hook it up to the QTreeView, and try to expand, I end up with the DisplayRole data of the parent being displayed for all the children. I've tried variations with deeper levels of children/parents, but it always shows me incorrect data for the children. In what seems like adding insult to injury, the WhatsThisRole data actually displays correctly - I can hover over the item and it will show me the correct text (it showing me the correct text was an addition by me in the data() function just to make sure I wasn't going crazy). Adding to the confusion, it appears that the selection model seems to be off a bit - I can't unclick items once selected, and selecting a child item selects all child items, same parent or not.

    It has to be something in my code. I have a bunch of logging/debugging lines set into it that will take a bit to back out (without losing them). I'm assuming though that the issue is in my Model implementation. Being my first time with this, I'm heavily leaning on the examples provided in books and online to get a good model implementation going. Code below.

    Qt Code:
    1. QModelIndex ParseList::index(int row, int column, const QModelIndex &parent) const
    2. {
    3. if(!head || row < 0 || column < 0)
    4. {
    5. return QModelIndex();
    6. }
    7. ParseNode * parentNode = nodeFromIndex(parent);
    8. ParseNode * childNode = parentNode->getChild(row);
    9. if(!childNode)
    10. {
    11. return QModelIndex();
    12. }
    13. QModelIndex temp = createIndex(row,column, childNode);
    14. return createIndex(row, column, childNode);
    15. }
    16.  
    17. QModelIndex ParseList::parent(const QModelIndex &child) const
    18. {
    19. ParseNode * node = nodeFromIndex(child);
    20. if(!node)
    21. return QModelIndex();
    22. ParseNode * parentNode = node->getParent();
    23. if(!parentNode)
    24. {
    25. return QModelIndex();
    26. }
    27. ParseNode * grandparentNode = parentNode->getParent();
    28. if(!grandparentNode)
    29. {
    30. return QModelIndex();
    31. }
    32. int row = grandparentNode->indexOf(parentNode);
    33. return createIndex(row, 0, parentNode);
    34. }
    35.  
    36. int ParseList::rowCount(const QModelIndex &parent) const
    37. {
    38. if (parent.column() > 0)
    39. {
    40. return 0;
    41. }
    42. ParseNode *parentNode = nodeFromIndex(parent);
    43. if(!parentNode)
    44. {
    45. return 0;
    46. }
    47. return parentNode->childCount();
    48. }
    49.  
    50. int ParseList::columnCount(const QModelIndex &parent) const
    51. {
    52. return ParseNode::dataColumns;
    53. }
    54.  
    55. QVariant ParseList::data(const QModelIndex &index, int role) const
    56. {
    57. if(!index.isValid())
    58. {
    59. return QVariant();
    60. }
    61. ParseNode * node = nodeFromIndex(index);
    62. if (!node)
    63. {
    64. return QVariant();
    65. }
    66. //TODO: Decide if smaller functions would be better
    67. // - small private functions
    68.  
    69. switch(index.column())
    70. {
    71. case 0:
    72. switch(role)
    73. {
    74. case Qt::DisplayRole:
    75. {
    76. return node->getPathString(); //stored as QString in node
    77. }
    78. case Qt::WhatsThisRole:
    79. case Qt::StatusTipRole:
    80. case Qt::ToolTipRole:
    81. return node->getPathString();//tr("Path of selected log"); //Added for debug
    82. default:
    83. return QVariant();
    84. }
    85. case 1:
    86. switch(role)
    87. {
    88. case Qt::DisplayRole:
    89. return node->getParseStatus() ? tr("Yes") : tr("No"); //Stored as bool in underlying class
    90. case Qt::WhatsThisRole:
    91. case Qt::StatusTipRole:
    92. case Qt::ToolTipRole:
    93. return node->getParseStatus() ?
    94. tr("Log has been parsed") :
    95. tr("Log has not been parsed");
    96. default:
    97. return QVariant();
    98. }
    99. case 2:
    100. switch(role)
    101. {
    102. case Qt::DisplayRole:
    103. switch(node->getPathType()) //public enum declared by ParseNode
    104. {
    105. case ParseNode::File:
    106. return tr("File");
    107. case ParseNode::FileButNotReadable:
    108. return tr("File - Unreadable");
    109. case ParseNode::Directory:
    110. return tr("Directory");
    111. case ParseNode::Invalid:
    112. return tr("Invalid");
    113. case ParseNode::Device:
    114. return tr("Device");
    115. default:
    116. return QVariant();
    117. }
    118. case Qt::WhatsThisRole:
    119. case Qt::StatusTipRole:
    120. case Qt::ToolTipRole:
    121. switch(node->getPathType())
    122. {
    123. case ParseNode::File:
    124. return tr("File from system");
    125. case ParseNode::FileButNotReadable:
    126. return tr("File exists, but is unreadable");
    127. case ParseNode::Directory:
    128. return tr("Directory from system");
    129. case ParseNode::Invalid:
    130. return tr("Invalid path specified");
    131. default:
    132. return QVariant();
    133. }
    134. default:
    135. return QVariant();
    136. }
    137. default:
    138. return QVariant();
    139. }
    140. }
    To copy to clipboard, switch view to plain text mode 

    And here's a screencap with things hooked up and at their worst:
    qtreeview1.jpg

    When I take out the "auto-reparenting" code, everything displays correctly on the top level, but again, all the children display the parent's data.

    Hopefully I've provided a good start of information - does anyone have any ideas? I can work on trimming things up further to get a .zip of what I'm seeing together as a compilable example.

    I'm using Qt 4.8.1 with MinGW as a compiler. The IDE I'm using is Qt Creator 2.4.1.

    Thanks in advance!

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


  3. #2
    Join Date
    Mar 2011
    Location
    Hyderabad, India
    Posts
    1,882
    Thanks
    3
    Thanked 452 Times in 435 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows
    Wiki edits
    15

    Default Re: QTreeView - Children Displaying Same Data As Parent, WhatsThisRole is Not

    I suspect a problem in either ParseList::parent()/ParseList::index() or both. My bet is on ParseList::index().

    I cannot suggest much without knowing what ParseNode does.

    What is head?

    Why grandparent / grandchild? Model can be implement with just parent relation.

    Can you post a compilable code?

    Anyway here are few tips:
    1. Store parent's row information in ParseNode, so that you get back parent info when ParseList::parent() is called. (I can guess that is what ParseNode does)
    2. Don't store QModelIndex in ParseNode.
    3. In ParseList::rowCount(), ParseList::columnCount(), check for valid index.
    When you know how to do it then you may do it wrong.
    When you don't know how to do it then it is not that you may do it wrong but you may not do it right.

  4. The following user says thank you to Santosh Reddy for this useful post:


  5. #3
    Join Date
    Jan 2013
    Location
    Wisconsin, USA
    Posts
    3
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Windows

    Default Re: QTreeView - Children Displaying Same Data As Parent, WhatsThisRole is Not

    What is head?
    Head was my mistake. It should be (and is now) properly named as root.

    Why grandparent / grandchild? Model can be implement with just parent relation.
    The grandparent was used to find the parent's index in the list. Even though I have removed it from this context, the ParseNode::myIndex() function does the same idea. The thought is what happens if items are inserted into the list? (Especially since I'm using QList and not QLinkedList and expect to support moving items around). Should I cache its position in ParseNode, and update the children when a change happens with the container?


    Anyway here are few tips:
    1. Store parent's row information in ParseNode, so that you get back parent info when ParseList:arent() is called. (I can guess that is what ParseNode does)
    2. Don't store QModelIndex in ParseNode.
    3. In ParseList::rowCount(), ParseList::columnCount(), check for valid index.
    1. Definitely can do (is what I described above what you are saying)?
    2. Yep. Also can do. The only times I created one outside of returning it from createIndex() is when I needed a temp object to dissect for debugging messages.
    3. How would I handle the root passed to rowCount()?

    Would you mean something like this?
    Qt Code:
    1. int ParseList::rowCount(const QModelIndex &parent) const
    2. {
    3. if(!parent.isValid())
    4. {
    5. return root->childCount();
    6. }
    7. if (parent.column() > 0)
    8. {
    9. return 0;
    10. }
    11. ParseNode *parentNode = nodeFromIndex(parent);
    12. if(!parentNode)
    13. return 0;
    14. return parentNode->childCount();
    15. }
    To copy to clipboard, switch view to plain text mode 

    A compilable example is attached.
    PreParser_Simple.zip

    Thanks very much for your reply and already useful help!

  6. The following user says thank you to vulcan for this useful post:


  7. #4
    Join Date
    Jan 2013
    Location
    Wisconsin, USA
    Posts
    3
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Windows

    Question Re: QTreeView - Children Displaying Same Data As Parent, WhatsThisRole is Not

    Does anyone have any ideas? I'm stuck going in circles and can't seem to find where the issue is. Is there any additional information I can provide, or perhaps things I can look for?

    Thanks!

  8. The following user says thank you to vulcan for this useful post:


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

    Default Re: QTreeView - Children Displaying Same Data As Parent, WhatsThisRole is Not

    This is surely invalid:

    Qt Code:
    1. void ParseNode::addChild(ParseNode *child)
    2. {
    3. if(child && children.indexOf(child) == 0); // <--- semicolon here?
    4. children.push_back(child);
    5. }
    To copy to clipboard, switch view to plain text mode 

    In my opinion it should be:

    Qt Code:
    1. void ParseNode::addChild(ParseNode *child)
    2. {
    3. if(child && children.indexOf(child) < 0) {
    4. children.push_back(child);
    5. child->myParent = this;
    6. }
    7. }
    To copy to clipboard, switch view to plain text mode 

    removeChild() has to be adjusted accordingly.

    ParseList::addPaths() looks invalid as well.... You're (incorrectly) informing the model that you'll be adding top-level items but then you're never attaching items you're adding to the root node (you only set it as the new item's parent but never add it to the root node's list of children thus parent() implementation can't work correctly). Maybe there are other problems as well, I can't see them right now.

    A minimal tree implementation looks more or less like this:

    Qt Code:
    1. #include <QApplication>
    2. #include <QAbstractItemModel>
    3. #include <QTreeView>
    4.  
    5. template<typename T> class Node {
    6. public:
    7. Node() { parentNode = 0; }
    8. ~Node() {
    9. if(parentNode)
    10. parentNode->removeChild(this);
    11. qDeleteAll(children);
    12. }
    13. void addChild(Node *n) {
    14. if(n->parentNode) {
    15. if(n->parentNode == this) return;
    16. n->parentNode->removeChild(n);
    17. n->parentNode = 0;
    18. }
    19. n->parentNode = this;
    20. children.append(n);
    21. }
    22. void removeChild(Node *n) {
    23. if(n->parentNode != this) return;
    24. n->parentNode = 0;
    25. children.removeOne(n);
    26. }
    27. int indexOf(Node *n) const { return children.indexOf(n); }
    28. Node *at(int idx) const { return children.at(idx); }
    29. int count() const { return children.count(); }
    30. Node *parent() const { return parentNode; }
    31. T data;
    32.  
    33. private:
    34. Node<T> *parentNode;
    35. QList<Node<T>*> children;
    36. };
    37.  
    38. template<typename T, int ColCount> class TreeModel : public QAbstractItemModel {
    39. public:
    40. TreeModel(QObject *parent = 0) : QAbstractItemModel(parent) {
    41. root = new Node<T>();
    42. }
    43. ~TreeModel() { delete root; }
    44. int rowCount(const QModelIndex &parent = QModelIndex()) const {
    45. return nodeFromIndex(parent)->count();
    46. }
    47. int columnCount(const QModelIndex &parent = QModelIndex()) const {
    48. return ColCount;
    49. }
    50. QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const {
    51. Node<T> *parentNode = nodeFromIndex(parent);
    52. if(row < 0 || row >= parentNode->count() || column < 0 || column >= ColCount) return QModelIndex();
    53. return createIndex(row, column, parentNode->at(row));
    54. }
    55. QModelIndex parent(const QModelIndex &child) const {
    56. Node<T> *childNode = nodeFromIndex(child);
    57. Node<T> *parentNode = childNode->parent();
    58. if(parentNode == root) return QModelIndex();
    59. Node<T> *grandparentNode = parentNode->parent();
    60. return createIndex(grandparentNode->indexOf(parentNode), 0, parentNode);
    61. }
    62. QVariant data(const QModelIndex &index, int role) const {
    63. qWarning("Reimplement in subclass");
    64. return QVariant();
    65. }
    66. protected:
    67. Node<T>* nodeFromIndex(const QModelIndex &idx) const {
    68. if(idx.isValid()) return static_cast<Node<T>*>(idx.internalPointer());
    69. return root;
    70. }
    71. Node<T> *root;
    72. };
    73.  
    74. class Model : public TreeModel<int, 1> {
    75. public:
    76. Model(QObject *parent = 0) : TreeModel(parent) {}
    77. QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const {
    78. if(index.column() != 0) return QVariant();
    79. if(role != Qt::DisplayRole && role != Qt::EditRole) return QVariant();
    80. Node<int> *n = nodeFromIndex(index);
    81. return n->data;
    82. }
    83. bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) {
    84. if(index.column() != 0) return false;
    85. if(role != Qt::DisplayRole && role != Qt::EditRole) return false;
    86. Node<int> *n = nodeFromIndex(index);
    87. if(n->data != value.toInt()) {
    88. n->data = value.toInt();
    89. emit dataChanged(index, index);
    90. return true;
    91. }
    92. return true;
    93. }
    94. void addItem(int dat) {
    95. beginInsertRows(QModelIndex(), root->count(), root->count());
    96. Node<int> *n = new Node<int>();
    97. n->data = dat;
    98. Node<int> *sn = new Node<int>();
    99. sn->data = dat*10;
    100. n->addChild(sn);
    101. root->addChild(n);
    102. endInsertRows();
    103. }
    104. };
    105.  
    106. class Adder : public QObject {
    107. public:
    108. Adder(Model *model, int interval = 2000) {
    109. m_model = model;
    110. startTimer(interval);
    111. }
    112. protected:
    113. void timerEvent(QTimerEvent *) {
    114. int val = rand() % 100;
    115. m_model->addItem(val);
    116. }
    117.  
    118. private:
    119. Model *m_model;
    120. };
    121.  
    122. int main(int argc, char *argv[])
    123. {
    124. QApplication a(argc, argv);
    125. Model model;
    126. // TreeModel<int,1> model;
    127. w.setModel(&model);
    128. w.show();
    129. Adder adder(&model, 1000);
    130. return a.exec();
    131. }
    To copy to clipboard, switch view to plain text mode 
    Last edited by wysota; 22nd January 2013 at 20:24.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  10. The following user says thank you to wysota for this useful post:


Similar Threads

  1. Hide Parent and show only children in QTreeView
    By y.s.bisht in forum Qt Programming
    Replies: 8
    Last Post: 19th January 2012, 09:51
  2. Replies: 0
    Last Post: 14th April 2010, 15:03
  3. disable a parent and children in a tree model
    By qt_gotcha in forum Newbie
    Replies: 4
    Last Post: 9th July 2009, 06:49
  4. Process the display data before displaying in QTreeView
    By babu198649 in forum Qt Programming
    Replies: 5
    Last Post: 23rd June 2009, 05:21

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.