Results 1 to 8 of 8

Thread: QTreeView: Drag and Drop

  1. #1
    Join Date
    Aug 2009
    Posts
    23
    Thanks
    3

    Default QTreeView: Drag and Drop

    Hi all,

    I am currently developping a custom tree view and I would like to enable drag and drop,
    From the tree picture attached to this message, I would like to be able to reorder 1rst level element.

    For now, I was able to enable the DnD function but I am somewhat lost with my model implementation.
    From the Model/View programming tutorial, I should implement all those methods: QAbstractItemModel::insertRows(), QAbstractItemModel::removeRows(), QAbstractItemModel::dropMimeData() and QAbstractItemModel::setData().

    With the available examples and tutorial, I don't understand this mechanism.
    In my case, I do have a tree model which contains nodes. Those nodes contains Tree Item (== data). When I insert a child to an existing node, I have the following code:
    Qt Code:
    1. bool ResourceTreeNode::insertChildren(int position, int count, CommonTreeItem* treeItem)
    2. {
    3. if (position < 0 || position > m_childrens.size()) {
    4. return false;
    5. }
    6.  
    7. for (int row = 0; row < count; ++row) {
    8. ResourceTreeNode* nodePtr = new ResourceTreeNode(treeItem, this);
    9. m_childrens.insert(position, nodePtr);
    10. }
    11.  
    12. return true;
    13. }
    To copy to clipboard, switch view to plain text mode 

    From my understanding I should write a method QAbstractItemModel::insertRows(int row, int count, const QModelIndex &parent) in my custom model which call the method ResourceTreeNode::insertChildren.
    The problem is that I don't get how I can create my TreeItem (contained in my tree node) from those attributes: row, count and parent index? I guess I should get the datas from the node I am dropping to create the new one but I really don't see the trick?

    Does somebody have an example using a custom model (with more than one column)?

    Sometimes I even read people who implement drag and drop event. Should I do that also? and why?

    Thank you for helping me.

    Best Regards,
    Lionel
    Attached Images Attached Images

  2. #2
    Join Date
    Jan 2006
    Location
    Innsbruck, Austria
    Posts
    62
    Thanks
    10
    Thanked 7 Times in 6 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: QTreeView: Drag and Drop

    You don't need to implement insertRows() nor removeRows() if you don't want. A super cute model would have it but it's ok to use your own methods. You just need to implement flags() and metaTypes() (in any case), dropMimeData() to allow dropping and mimeData() to allow dragging. And of course set the properties on the view to their proper values.

    Qt Code:
    1. m_view->viewport()->setAcceptsDrops(true);
    2. m_view->setDragEnabled(true);
    3. m_view->setDragDropMode(QAbstractItemView::InternalMove);
    To copy to clipboard, switch view to plain text mode 

    When you get an index, you may use index.internalPointer() to get a pointer to your TreeNode. You have to cast it to your TreeNode. Take a look at the Simple Tree Model example:

    Qt Code:
    1. QModelIndex TreeModel::parent(const QModelIndex &index) const
    2. {
    3. if (!index.isValid())
    4. return QModelIndex();
    5.  
    6. TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
    7. TreeItem *parentItem = childItem->parent();
    8.  
    9. if (parentItem == rootItem)
    10. return QModelIndex();
    11.  
    12. return createIndex(parentItem->row(), 0, parentItem);
    13. }
    To copy to clipboard, switch view to plain text mode 

    So, in mimeData() you get the tree node and construct some way to represent the data. You may use XML, QDataStream or whatever you want, but you need to create a QByteArray that you can later read. It might even be just the number of your row or some id. Then create a QMimeData object and add your data as a custom MIME type.

    In dropMimeData() you also get an index. Check whether it's valid and index.row() is not -1. If it's -1, the data should be appended at the end of your parent node. Otherwise, insert it in the right row as a child of the pointer you get. You will need to create the node you're going to insert. To fill it with the right data, you need to "interpret" the mime type you previously constructed. If it was XML, then you will need to parse it. If it's a QDataStream, then unserialize your data.

    Take a look at the implementations in Using Drag and Drop with Item Views, which explains it quite clearly.
    Last edited by vfernandez; 28th August 2009 at 20:31.

  3. The following user says thank you to vfernandez for this useful post:

    laugusti (1st September 2009)

  4. #3
    Join Date
    Aug 2009
    Posts
    23
    Thanks
    3

    Default Re: QTreeView: Drag and Drop

    Hi vfernandez,

    Thank you for your reply. It really clears my mind.

    I am able to drag and drop my items but I believe I have an index error.
    I use a QSortFilterProxyModel between the view and the "real" model so I wonder if I must implement QAbstractItemModel::mimeTypes(), QAbstractItemModel::mimeData(), and QAbstractItemModel::dropMimeData() in the proxy model instead of the "real" model?

    Did you face this problem already?

    Thanks again.

    Best regards,
    Lionel

  5. #4
    Join Date
    Aug 2009
    Posts
    23
    Thanks
    3

    Default Re: QTreeView: Drag and Drop

    I tried what I was talking about in the previous post and the error message: "QSortFilterProxyModel: invalid inserted rows reported by source model" disappeared.

    However I still have a problem with the indexes!

    I keep investigating ...

    Lionel

  6. #5
    Join Date
    Feb 2008
    Posts
    98
    Thanks
    2
    Thanked 24 Times in 24 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: QTreeView: Drag and Drop

    You don't need to implement anything in the proxy model because it's just a proxy. When the view calls the proxy's mimeTypes(), mimeData() and stuff, the proxy will query your model's mimeTypes(), mimeData() and so on, and return the results. It translates the proxy indexes to your own model's indexes (e.g. if the proxy is filtering row #2 and the view queries the proxy for its row #4, the proxy will query your model for row #5 because #5 in your model is #4 for the proxy).

    If you post your code here I can take a look at it and help you with it.

  7. #6
    Join Date
    Aug 2009
    Posts
    23
    Thanks
    3

    Default Re: QTreeView: Drag and Drop

    Hi Victor,

    I moved back the DnD specific code from the QSortFilterProxyModel to the QAbstractItemModel

    Here is the piece of code about DnD:

    Qt Code:
    1. //===================================================================
    2. QMimeData* QResourceTreeModel::mimeData(const QModelIndexList &indexes) const
    3. {
    4. QMimeData *mimeDataPtr = new QMimeData();
    5. QByteArray encodedData;
    6. QDataStream stream(&encodedData, QIODevice::WriteOnly);
    7.  
    8. /* Store row id list */
    9. QList<int> rowIdList;
    10. int rowId;
    11. foreach (QModelIndex index, indexes) {
    12. if (index.isValid()) {
    13. rowId = index.row();
    14.  
    15. if (!rowIdList.contains(rowId)) {
    16. rowIdList << rowId;
    17. stream << QVariant(rowId).toString();
    18. }
    19. }
    20. }
    21.  
    22. mimeDataPtr->setData("application/text.list", encodedData);
    23. return mimeDataPtr;
    24. }
    25.  
    26. //===================================================================
    27. bool QResourceTreeModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row,
    28. int column, const QModelIndex &parent)
    29. {
    30. if (action == Qt::IgnoreAction) {
    31. return true;
    32. }
    33.  
    34. if (column > QResourceTreeModel::NB_COLUMN) {
    35. return false;
    36. }
    37.  
    38. int position;
    39.  
    40. if (row != -1) {
    41. position = row;
    42. } else if (parent.isValid()) {
    43. position = parent.row();
    44. } else {
    45. position = rowCount(QModelIndex());
    46. }
    47.  
    48. QByteArray encodedData = data->data("application/text.list");
    49. QDataStream stream(&encodedData, QIODevice::ReadOnly);
    50.  
    51. /* Retrieve row id */
    52. QList<int> rowIdList;
    53. while (!stream.atEnd()) {
    54. QString text;
    55. stream >> text;
    56. rowIdList << text.toInt();
    57. }
    58.  
    59. /* Insert rows (one by one) */
    60. foreach(int rowId, rowIdList) {
    61. insertRow(position, parent, rowId);
    62. }
    63.  
    64. return true;
    65. }
    66.  
    67. //===================================================================
    68. bool QResourceTreeModel::insertRow(int position, const QModelIndex &parent, int rowId)
    69. {
    70. bool success;
    71.  
    72. beginInsertRows(parent, position, position);
    73. success = m_rootPtr->insertChildren(position, m_rootPtr->child(rowId)->getTreeItem());
    74. endInsertRows();
    75.  
    76. /* Add masks */
    77. // m_rootPtr->child(position)->insertChildren(0, m_rootPtr->child(rowId)->child(0)->getTreeItem());
    78. // m_rootPtr->child(position)->insertChildren(1, m_rootPtr->child(rowId)->child(1)->getTreeItem());
    79.  
    80. return success;
    81. }
    To copy to clipboard, switch view to plain text mode 

    In insertRow(), I retrieve the data from the row I am actually dragging (== rowId) and insert it in a new row at position.
    The row is duplicated at the right index but sometimes the following message appears:
    QSortFilterProxyModel: invalid inserted rows reported by source model


    Thanks again,
    Lionel

  8. #7
    Join Date
    Aug 2009
    Posts
    23
    Thanks
    3

    Default Re: QTreeView: Drag and Drop

    Victor,

    Don't waste your time, after reading again and again the insertRow() method I found my mistake.

    Here is the fixed method:

    Qt Code:
    1. //===================================================================
    2. bool QResourceTreeModel::insertRow(int position, const QModelIndex &parent, int rowId)
    3. {
    4. bool success;
    5.  
    6. /* parent => parent.parent() */
    7. beginInsertRows(parent.parent(), position, position);
    8. success = m_rootPtr->insertChildren(position, m_rootPtr->child(rowId)->getTreeItem());
    9. endInsertRows();
    10.  
    11. /* Add masks */
    12. // m_rootPtr->child(position)->insertChildren(0, m_rootPtr->child(rowId)->child(0)->getTreeItem());
    13. // m_rootPtr->child(position)->insertChildren(1, m_rootPtr->child(rowId)->child(1)->getTreeItem());
    14.  
    15. return success;
    16. }
    To copy to clipboard, switch view to plain text mode 

    I am directly inserting items under the Root node so I was wrong when calling QAbstractItemModel::beginInsertRows() on the parent! While it must be the parent of the parent (i.e the root node).

    Anyway thank you very much for your first post, it really helped me go through this DnD infrastructure.

    Best Regards,
    Lionel

  9. #8
    Join Date
    Mar 2010
    Posts
    1
    Qt products
    Qt4
    Platforms
    Windows

    Question Re: QTreeView: Drag and Drop

    Hi all!

    I am most probably asking stupid questions but I am new to Qt instrumentary so please don't judje me too hard *blush*

    I am also implementing QTreeView with drag&drop for item reordering and I don't get one thing: how will I be able to move nodes under the root node since it is invisible?

    To make myself more clear, here's the illustration:
    Qt Code:
    1. [root]
    2. | - A
    3. | - D
    4. | - E
    5. | - B
    6. | - C
    To copy to clipboard, switch view to plain text mode 

    And say I want to move D to the root _after_ B. How shall I do this? If I drag the D over the B then D will become child of B, won't it?

    And another question: inserting new elements to the tree. I've read the Simple Tree Model Example and Editable Tree Model Example but one thing is left unclear to me: how do I insert a new element as a child of the root element? As far as I understood the position of the new element is determined by current selection in the QTreeView: the new element is inserted as a child of selected element. But I can't select the root element since it's invisible! How is this issue resolved? Maybe just make a pseudo-root inside the real root and place all my items in it?

    Thanks in advance!

Similar Threads

  1. Preserving structure in QTreeView drag and drop
    By Tito Serenti in forum Qt Programming
    Replies: 1
    Last Post: 14th February 2009, 04:39
  2. Drag and drop in QTreeView
    By Valheru in forum Qt Programming
    Replies: 3
    Last Post: 27th July 2008, 10:36
  3. Drag and Drop on Whitespace of QTreeView
    By T1c4L in forum Qt Programming
    Replies: 6
    Last Post: 14th May 2008, 14:29
  4. Drag & drop items on the same QTreeView
    By wind in forum Qt Programming
    Replies: 2
    Last Post: 11th October 2006, 15:29
  5. Drag & drop for QTreeView
    By yogeshm02 in forum Qt Programming
    Replies: 2
    Last Post: 30th January 2006, 15:32

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.