Results 1 to 16 of 16

Thread: QListView drag and drop

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Jan 2020
    Posts
    53
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QListView drag and drop

    Quote Originally Posted by d_stranz View Post
    Can you post some code from your original implementation where asking for the string list returns the wrong result?
    My latest version looks like this (I use KDevelop/cmake):
    Qt Code:
    1. #ifndef LISTVIEWDRAGDROP_H
    2. #define LISTVIEWDRAGDROP_H
    3.  
    4. #include <QMainWindow>
    5. #include <QPushButton>
    6. #include "modmodel.h"
    7.  
    8. namespace Ui {
    9. class ListViewDragDrop;
    10. }
    11.  
    12. class QListView;
    13. class ModModel;
    14. class ModListView;
    15.  
    16. class ListViewDragDrop : public QMainWindow
    17. {
    18. Q_OBJECT
    19.  
    20. public:
    21. explicit ListViewDragDrop(QWidget *parent = 0);
    22. ~ListViewDragDrop();
    23.  
    24. void receive_strings();
    25.  
    26. private:
    27. //void slot_test();
    28. Ui::ListViewDragDrop *ui;
    29. QPushButton *m_button;
    30.  
    31. public:
    32. //QStringListModel *m_ModelA;
    33. //QStringListModel *m_ModelB;
    34. ModModel *m_ModelA;
    35. ModModel *m_ModelB;
    36.  
    37. ModListView *m_ViewA;
    38. ModListView *m_ViewB;
    39. };
    40.  
    41. #include "listviewdragdrop.h"
    42. #include "ui_listviewdragdrop.h"
    43. #include <QListView>
    44. #include <QStringListModel>
    45. #include <QSplitter>
    46. #include <QDebug>
    47. #include "modmodel.h"
    48. #include "modlistview.h"
    49.  
    50. ListViewDragDrop::ListViewDragDrop(QWidget *parent) :
    51. QMainWindow(parent),
    52. ui(new Ui::ListViewDragDrop)
    53. {
    54. ui->setupUi(this);
    55.  
    56. QStringList listA;
    57. listA << "A1" << "A2" << "A3" << "A4";
    58.  
    59. //m_ModelA = new QStringListModel(this);
    60. m_ModelA = new ModModel(this);
    61. m_ModelA->setStringList(listA);
    62.  
    63. QStringList listB;
    64. listB << "B1" << "B2" << "B3" << "B4";
    65.  
    66. //m_ModelB = new QStringListModel(this);
    67. m_ModelB = new ModModel(this);
    68. m_ModelB->setStringList(listB);
    69.  
    70. QSplitter * pSplitter = new QSplitter( Qt::Horizontal );
    71.  
    72. m_ViewA = new ModListView(this);
    73. m_ViewA->setModel( m_ModelA );
    74. m_ViewA->setDragEnabled( true );
    75. m_ViewA->setAcceptDrops( true );
    76.  
    77. m_ViewB = new ModListView(this);
    78. m_ViewB->setModel( m_ModelB );
    79. m_ViewB->setDragEnabled( true );
    80. m_ViewB->setAcceptDrops( true );
    81.  
    82. m_button = new QPushButton("Test");
    83.  
    84. pSplitter->addWidget(m_ViewA);
    85. pSplitter->addWidget(m_ViewB);
    86. pSplitter->addWidget(m_button);
    87. setCentralWidget( pSplitter );
    88. }
    89.  
    90. ListViewDragDrop::~ListViewDragDrop()
    91. {
    92. delete ui;
    93. }
    94.  
    95. void ListViewDragDrop::receive_strings()
    96. {
    97. int ARC = m_ModelA->rowCount();
    98. int BRC = m_ModelB->rowCount();
    99. QStringList listeA = m_ModelA->stringList();
    100. QStringList listeB = m_ModelB->stringList();
    101. qDebug() << "ListViewDragDrop::receive_strings: rowo count:" << ARC << "listeB" << listeA << BRC << "listeB" << listeB;
    102. }
    103.  
    104. #ifndef MODLISTVIEW_H
    105. #define MODLISTVIEW_H
    106.  
    107. #include <QListView>
    108. #include <QDropEvent>
    109.  
    110. class ListViewDragDrop;
    111.  
    112. class ModListView : public QListView
    113. {
    114. Q_OBJECT
    115.  
    116. public:
    117. ModListView(ListViewDragDrop *parent);
    118. ~ModListView();
    119.  
    120. private:
    121. void dropEvent(QDropEvent *e);
    122. ListViewDragDrop *m_parent;
    123. QPoint startPos;
    124. };
    125.  
    126. #endif // MODLISTVIEW_H
    127.  
    128.  
    129. #include "modlistview.h"
    130. #include <QDebug>
    131. #include <QDropEvent>
    132. #include <QApplication>
    133. #include <QMimeData>
    134. #include <QAbstractItemView>
    135. #include "listviewdragdrop.h"
    136.  
    137. ModListView::ModListView(ListViewDragDrop *parent) : m_parent(parent)
    138. {
    139. }
    140.  
    141. ModListView::~ModListView()
    142. {
    143. }
    144.  
    145. void ModListView::dropEvent(QDropEvent *e)
    146. {
    147. qDebug() << "ModListView::dropEvent: rowo count:";
    148. QListView::dropEvent(e);
    149. m_parent->receive_strings();
    150. }
    151.  
    152. #ifndef MODMODEL_H
    153. #define MODMODEL_H
    154.  
    155. #include <QStringListModel>
    156. #include <QDropEvent>
    157.  
    158. class ListViewDragDrop;
    159.  
    160. class ModModel : public QStringListModel
    161. {
    162. Q_OBJECT
    163.  
    164. public:
    165. ModModel(ListViewDragDrop *parent);
    166. ~ModModel();
    167.  
    168. virtual Qt::DropActions supportedDropActions() const override
    169. {
    170. return Qt::MoveAction;
    171. }
    172.  
    173. private:
    174. ListViewDragDrop *m_parent;
    175.  
    176. };
    177.  
    178. #endif // MODMODEL_H
    179.  
    180. #include "modmodel.h"
    181. #include <QDebug>
    182. #include "listviewdragdrop.h"
    183.  
    184. ModModel::ModModel(ListViewDragDrop *parent) : m_parent(parent)
    185. {
    186. }
    187.  
    188. ModModel::~ModModel()
    189. {
    190. }
    To copy to clipboard, switch view to plain text mode 

  2. #2
    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: QListView drag and drop

    void ModListView::dropEvent(QDropEvent *e)
    {
    qDebug() << "ModListView::dropEvent: rowo count:";
    QListView::dropEvent(e);
    m_parent->receive_strings();
    }
    You aren't letting control return to the event loop before trying to retrieve the listview contents. In particular, the things that the model and view needed to do to communicate with each other what has been dropped, and for the model to signal its view that the model has changed.

    In your MainWindow class, make a slot to handle the QStringListModel' s dataChanged() signal (see QAbstractItemModel). Connect the model's signal to your slot. In that slot, you should be able to retrieve the updated string list.

    I think you can probably get rid of your ModListView class completely. The standard QListView does what you need. You just need to use it correctly.
    <=== 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.

  3. #3
    Join Date
    Jan 2020
    Posts
    53
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QListView drag and drop

    I tried hooking up to dataChanged, as per your suggestion, but the behaviour is exactly the same as before. The listview I move from (listA) is visually updated (so the model must also be correctly updated), but what I read out of the from the model (in a slot connected to dataChanged) is not correct. If I drag an item in the destination view (viewB) to another row, then the source view (viewA) data is updated, but now the destination data read out in modelB is not; the source item is not deleted (visually it is), so I get one row too many. If I now move a row in listA, then listB is updated, but now list A is behind. So I am still scratching my head...

  4. #4
    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: QListView drag and drop

    I modified the program I posted above to add slots for various model methods (dataChanged, rowsInserted, rowsRemoved):

    Qt Code:
    1. // ListViewDragDrop.h
    2. #ifndef LISTVIEWDRAGDROP_H
    3. #define LISTVIEWDRAGDROP_H
    4.  
    5. #include <QtWidgets/QMainWindow>
    6.  
    7. class QListView;
    8.  
    9. class ListViewDragDrop : public QMainWindow
    10. {
    11. Q_OBJECT
    12.  
    13. public:
    14. ListViewDragDrop(QWidget *parent = 0);
    15. ~ListViewDragDrop();
    16.  
    17. protected slots:
    18. void onModelADataChanged( const QModelIndex & topLeft, const QModelIndex & bottomRight );
    19. void onModelARowsInserted( const QModelIndex & parent, int first, int last );
    20. void onModelARowsRemoved( const QModelIndex & parent, int first, int last );
    21. void onModelBDataChanged( const QModelIndex & topLeft, const QModelIndex & bottomRight );
    22. void onModelBRowsInserted( const QModelIndex & parent, int first, int last );
    23. void onModelBRowsRemoved( const QModelIndex & parent, int first, int last );
    24.  
    25. private:
    26. void dumpStringList( const QString & where, const QString & modelName, QStringListModel * pModel );
    27.  
    28. private:
    29. QStringListModel * mpModelA;
    30. QStringListModel * mpModelB;
    31.  
    32. QListView * mpViewA;
    33. QListView * mpViewB;
    34. };
    35.  
    36. #endif // LISTVIEWDRAGDROP_H
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. // ListViewDragDrop.cpp
    2. #include "ListViewDragDrop.h"
    3.  
    4. #include "MoveListModel.h"
    5.  
    6. #include <QListView>
    7. #include <QStringListModel>
    8. #include <QSplitter>
    9. #include <QDebug>
    10.  
    11. ListViewDragDrop::ListViewDragDrop(QWidget *parent)
    12. : QMainWindow(parent)
    13. {
    14. QStringList listA;
    15. listA << "String A1" << "String A2" << "String A3" << "String A4";
    16.  
    17. mpModelA = new MoveListModel( this );
    18. connect( mpModelA, &QAbstractItemModel::dataChanged, this, &ListViewDragDrop::onModelADataChanged );
    19. connect( mpModelA, &QAbstractItemModel::rowsInserted, this, &ListViewDragDrop::onModelARowsInserted );
    20. connect( mpModelA, &QAbstractItemModel::rowsRemoved, this, &ListViewDragDrop::onModelARowsRemoved );
    21. mpModelA->setStringList( listA );
    22.  
    23. QStringList listB;
    24. listB << "String B1" << "String B2" << "String B3" << "String B4";
    25.  
    26. mpModelB = new QStringListModel( this );
    27. connect( mpModelB, &QAbstractItemModel::dataChanged, this, &ListViewDragDrop::onModelBDataChanged );
    28. connect( mpModelB, &QAbstractItemModel::rowsInserted, this, &ListViewDragDrop::onModelBRowsInserted );
    29. connect( mpModelB, &QAbstractItemModel::rowsRemoved, this, &ListViewDragDrop::onModelBRowsRemoved );
    30. mpModelB->setStringList( listB );
    31.  
    32. QSplitter * pSplitter = new QSplitter( Qt::Horizontal );
    33.  
    34. mpViewA = new QListView();
    35. mpViewA->setModel( mpModelA );
    36. mpViewA->setDragEnabled( true );
    37. mpViewA->setAcceptDrops( true );
    38.  
    39. mpViewB = new QListView();
    40. mpViewB->setModel( mpModelB );
    41. mpViewB->setDragEnabled( true );
    42. mpViewB->setAcceptDrops( true );
    43.  
    44. pSplitter->addWidget( mpViewA );
    45. pSplitter->addWidget( mpViewB );
    46. setCentralWidget( pSplitter );
    47.  
    48. }
    49.  
    50. ListViewDragDrop::~ListViewDragDrop()
    51. {
    52. }
    53.  
    54. void ListViewDragDrop::onModelADataChanged( const QModelIndex & topLeft, const QModelIndex & bottomRight )
    55. {
    56. dumpStringList( "onModelADataChanged", "Model A", mpModelA );
    57. }
    58.  
    59. void ListViewDragDrop::onModelARowsInserted( const QModelIndex & parent, int first, int last )
    60. {
    61. dumpStringList( "onModelARowsInserted", "Model A", mpModelA );
    62. }
    63.  
    64. void ListViewDragDrop::onModelARowsRemoved( const QModelIndex & parent, int first, int last )
    65. {
    66. dumpStringList( "onModelARowsRemoved", "Model A", mpModelA );
    67. }
    68.  
    69. void ListViewDragDrop::onModelBDataChanged( const QModelIndex & topLeft, const QModelIndex & bottomRight )
    70. {
    71. dumpStringList( "onModelBDataChanged", "Model B", mpModelB );
    72. }
    73.  
    74. void ListViewDragDrop::onModelBRowsInserted( const QModelIndex & parent, int first, int last )
    75. {
    76. dumpStringList( "onModelBRowsInserted", "Model B", mpModelB );
    77. }
    78.  
    79. void ListViewDragDrop::onModelBRowsRemoved( const QModelIndex & parent, int first, int last )
    80. {
    81. dumpStringList( "onModelBRowsRemoved", "Model B", mpModelB );
    82. }
    83.  
    84. void ListViewDragDrop::dumpStringList( const QString & where, const QString & modelName, QStringListModel * pModel )
    85. {
    86. if ( pModel )
    87. qDebug() << "From " << where << " - Current string list for " << modelName << ":" << pModel->stringList();
    88. }
    To copy to clipboard, switch view to plain text mode 
    Run this code in the debugger.

    No debug output for the initial setStringList calls. But when I added slots for the modelReset() signals, I get this on startup:

    Qt Code:
    1. From "onModelAReset" - Current string list for "Model A" : ("String A1", "String A2", "String A3", "String A4")
    2. From "onModelBReset" - Current string list for "Model B" : ("String B1", "String B2", "String B3", "String B4")
    To copy to clipboard, switch view to plain text mode 

    if I click and drag "String A4" from model A to model B (making a copy of the item from A), I see this output:

    Qt Code:
    1. From "onModelBRowsInserted" - Current string list for "Model B" : ("String B1", "String B2", "String B3", "String B4", "")
    2. From "onModelBDataChanged" - Current string list for "Model B" : ("String B1", "String B2", "String B3", "String B4", "String A4")
    To copy to clipboard, switch view to plain text mode 

    Likewise, if I CLICK-DRAG "String B4" from model B to model A (again making a copy), I see this:

    Qt Code:
    1. From "onModelARowsInserted" - Current string list for "Model A" : ("String A1", "String A2", "String A3", "String A4", "")
    2. From "onModelADataChanged" - Current string list for "Model A" : ("String A1", "String A2", "String A3", "String A4", "String B4")
    To copy to clipboard, switch view to plain text mode 

    If I SHIFT-CLICK-DRAG "String A1" from model A to model B (moving it), I see this output:
    Qt Code:
    1. From "onModelBRowsInserted" - Current string list for "Model B" : ("String B1", "String B2", "String B3", "String B4", "String A4", "")
    2. From "onModelBDataChanged" - Current string list for "Model B" : ("String B1", "String B2", "String B3", "String B4", "String A4", "String A1")
    3. From "onModelARowsRemoved" - Current string list for "Model A" : ("String A2", "String A3", "String A4", "String B4")
    To copy to clipboard, switch view to plain text mode 

    So, it looks like the following is happening:

    - rowsInserted is called when the item is dropped, and the strin list shows that there is an entry added to the model, but it is not complete
    - dataChanged is called after rowsInserted, and now the model is complete
    - rowsRemoved is called when an item is moved out of the source model, but dataChanged is not called for that model

    This makes sense, because the dataChanged signal contains indexes for items that are presently in the model. If you remove one, it no longer has a valid index in the model.
    <=== 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. #5
    Join Date
    Jan 2020
    Posts
    53
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QListView drag and drop

    Quote Originally Posted by d_stranz View Post
    So, it looks like the following is happening:

    - rowsInserted is called when the item is dropped, and the strin list shows that there is an entry added to the model, but it is not complete
    - dataChanged is called after rowsInserted, and now the model is complete
    - rowsRemoved is called when an item is moved out of the source model, but dataChanged is not called for that model

    This makes sense, because the dataChanged signal contains indexes for items that are presently in the model. If you remove one, it no longer has a valid index in the model.
    So that's the way it works. Yes, it does make sense now when you point it out. I do not have enough experience with Qt to be able to read this behaviour out of the docs, so you help is greatly appreciated. When I now connect to "rowsRemoved" the drag and drop behaves the way I would expect, as I now read the correct data from the models. It is necessary though to connect "rowsRemoved" from both models, in order to cater for row reordering within a single listview.

  6. #6
    Join Date
    Jan 2020
    Posts
    53
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QListView drag and drop

    I thought I had fixed this, but discovered that if I drag an item (string) from listA and drop it over an existing item in listB then the existing item is overwritten. From the docs it seems that this behaviour can be prevented by calling setDragDropOverwriteMode(false), but that doesn't change anything. What is the correct way to prevent overwriting an existing item?

  7. #7
    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: QListView drag and drop

    According to the QAbstractItemView docs:

    dragDropOverwriteMode : bool

    This property holds the view's drag and drop behavior

    If its value is true, the selected data will overwrite the existing item data when dropped, while moving the data will clear the item. If its value is false, the selected data will be inserted as a new item when the data is dropped. When the data is moved, the item is removed as well.

    The default value is false, as in the QListView and QTreeView subclasses. In the QTableView subclass, on the other hand, the property has been set to true.

    Note: This is not intended to prevent overwriting of items. The model's implementation of flags() should do that by not returning Qt::ItemIsDropEnabled.
    The last sentence (Note) is the important one. If you have already derived from QStringListModel to implement supportedDropActions(), then you will also need to implement the flags() method in your model class to mask out the ItemIsDropEnabled flag. ( return QStringListModel::flags( index ) & ~Qt::ItemIsDropEnabled; ) [Edit: added "index" argument to superclass call]

    This doesn't make a lot of sense to me. It sounds like dragDropOverwriteMode applies to the entire model when something is dropped, not individual items, but if the default is true for QTableView, who would want behavior when dropping something erased the entire table?
    Last edited by d_stranz; 20th August 2020 at 22:55.
    <=== 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. #8
    Join Date
    Jan 2020
    Posts
    53
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QListView drag and drop

    Quote Originally Posted by d_stranz View Post
    According to the QAbstractItemView docs:



    The last sentence (Note) is the important one. If you have already derived from QStringListModel to implement supportedDropActions(), then you will also need to implement the flags() method in your model class to mask out the ItemIsDropEnabled flag. ( return QStringListModel::flags() & ~Qt::ItemIsDropEnabled; )

    This doesn't make a lot of sense to me. It sounds like dragDropOverwriteMode applies to the entire model when something is dropped, not individual items, but if the default is true for QTableView, who would want behavior when dropping something erased the entire table?
    Do you mean?
    Qt Code:
    1. virtual Qt::ItemFlags flags(const QModelIndex &index) const override
    2. {
    3. return QStringListModel::flags() | ~Qt::ItemIsDropEnabled;
    4. }
    To copy to clipboard, switch view to plain text mode 
    Probably not, as it doesn't compile.

  9. #9
    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: QListView drag and drop

    No, you should be returning the logical AND of whatever flags there already are (QStringListModel:: flags()) with the bitwise NOT of Qt:: ItemIsDropEnabled. My original post had a logical OR, and I realized the mistake and fixed it a bit later.

    Qt Code:
    1. return QStringListModel::flags( index ) & ~Qt::ItemIsDropEnabled;
    To copy to clipboard, switch view to plain text mode 

    Your code doesn't compile because you left out the "index" argument to the superclass call. In this case, I also left out the argument, but seriously, you have to look at what the compiler is telling you and figure out what is wrong.
    <=== 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.

  10. #10
    Join Date
    Jan 2020
    Posts
    53
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QListView drag and drop

    Quote Originally Posted by d_stranz View Post
    No, you should be returning the logical AND of whatever flags there already are (QStringListModel:: flags()) with the bitwise NOT of Qt:: ItemIsDropEnabled.
    Qt Code:
    1. return QStringListModel::flags( index ) & ~Qt::ItemIsDropEnabled;
    To copy to clipboard, switch view to plain text mode 
    Yes, you are right. However, this prevents all drop actions in the view. The solution to the problem, which can be found here, is to AND in Qt::ItemIsDropEnabled only if (index.isValid()). So now the problem is fixed.

Similar Threads

  1. Need help regarding drag and drop in QListView.
    By sonulohani in forum Qt Programming
    Replies: 6
    Last Post: 8th May 2016, 09:13
  2. qt 3.3 qlistview drag and drop
    By harishiva in forum Qt Programming
    Replies: 0
    Last Post: 21st September 2011, 12:46
  3. Replies: 2
    Last Post: 13th October 2010, 21:51
  4. Drag and Drop from QListView to QGraphicsView
    By NoRulez in forum Qt Programming
    Replies: 0
    Last Post: 20th August 2009, 14:26
  5. Problem with drag&drop in qlistview
    By mambo in forum Qt Programming
    Replies: 2
    Last Post: 11th September 2006, 16:14

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.