Results 1 to 16 of 16

Thread: QListView drag and drop

  1. #1
    Join Date
    Jan 2020
    Posts
    47
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default QListView drag and drop

    I have a dialogue with two QListView objects A and B, and I want to drag text strings from A to B, and vice verse. In my sub-classed QListview, ListView, I have populated A's model from a QStringList, and enabled the obejcts thus:

    Qt Code:
    1. m_model_A = new QStringListModel;
    2. m_listview_A = new ListView(this);
    3. m_listview_A->setModel(m_model_A);
    4. m_listview_A->setSelectionMode(QAbstractItemView::ExtendedSelection);
    5. m_listview_A->setDragEnabled(true);
    6. m_listview_A->setAcceptDrops(true);
    7. m_listview_A->setDropIndicatorShown(true);
    To copy to clipboard, switch view to plain text mode 
    Idem for m_model_B / m_listview_B.

    I have found several examples of drag and drop using QListWidget, but not for QListView. So I am unsure about what is already implemented in QListView. Subclassing QListView and reimplementing only dropEvent this function is called, but not if I also reimplement dragEnterEvent. Which cased my to wonder if dragEnterEvent perhaps does not need to be reimplemented for QListView? Also,
    Qt Code:
    1. if ( event->mimeData()->hasText())
    To copy to clipboard, switch view to plain text mode 
    in dropEvent(QDropEvent *event) evaluates to false. So, do I need to reimplement dragEnterEvent and perhaps mousePressEvent when using QListView?

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

    Default Re: QListView drag and drop

    This simple program supports drag and drop for list A (left) to list B (right) and vice versa. No extra code.

    Qt Code:
    1. // main.cpp
    2. #include "ListViewDragDrop.h"
    3. #include <QtWidgets/QApplication>
    4.  
    5. int main(int argc, char *argv[])
    6. {
    7. QApplication a(argc, argv);
    8. ListViewDragDrop w;
    9. w.show();
    10. return a.exec();
    11. }
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. // ListViewDragDrop.h
    2.  
    3. #ifndef LISTVIEWDRAGDROP_H
    4. #define LISTVIEWDRAGDROP_H
    5.  
    6. #include <QtWidgets/QMainWindow>
    7.  
    8. class QListView;
    9.  
    10. class ListViewDragDrop : public QMainWindow
    11. {
    12. Q_OBJECT
    13.  
    14. public:
    15. ListViewDragDrop(QWidget *parent = 0);
    16. ~ListViewDragDrop();
    17.  
    18. private:
    19. QStringListModel * mpModelA;
    20. QStringListModel * mpModelB;
    21.  
    22. QListView * mpViewA;
    23. QListView * mpViewB;
    24. };
    25.  
    26. #endif // LISTVIEWDRAGDROP_H
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. // ListViewDragDrop.cpp
    2.  
    3. #include "ListViewDragDrop.h"
    4.  
    5. #include <QListView>
    6. #include <QStringListModel>
    7. #include <QSplitter>
    8.  
    9. ListViewDragDrop::ListViewDragDrop(QWidget *parent)
    10. : QMainWindow(parent)
    11. {
    12. QStringList listA;
    13. listA << "String A1" << "String A2" << "String A3" << "String A4";
    14.  
    15. mpModelA = new QStringListModel( this );
    16. mpModelA->setStringList( listA );
    17.  
    18. QStringList listB;
    19. listB << "String B1" << "String B2" << "String B3" << "String B4";
    20.  
    21. mpModelB = new QStringListModel( this );
    22. mpModelB->setStringList( listB );
    23.  
    24. QSplitter * pSplitter = new QSplitter( Qt::Horizontal );
    25.  
    26. mpViewA = new QListView();
    27. mpViewA->setModel( mpModelA );
    28. mpViewA->setDragEnabled( true );
    29. mpViewA->setAcceptDrops( true );
    30.  
    31. mpViewB = new QListView();
    32. mpViewB->setModel( mpModelB );
    33. mpViewB->setDragEnabled( true );
    34. mpViewB->setAcceptDrops( true );
    35.  
    36. pSplitter->addWidget( mpViewA );
    37. pSplitter->addWidget( mpViewB );
    38. setCentralWidget( pSplitter );
    39.  
    40. }
    41.  
    42. ListViewDragDrop::~ListViewDragDrop()
    43. {
    44. }
    To copy to clipboard, switch view to plain text mode 

    Note that the default CLICK-DRAG operation is to copy items from one list to the other, so you get duplicates. If you want to move from one list to the other, then you need to derive from QStringListModel and override the QStringListModel::supportedDropActions() method to return Qt::MoveAction
    Qt Code:
    1. #ifndef MOVELISTMODEL_H
    2. #define MOVELISTMODEL_H
    3.  
    4. #include <QStringListModel>
    5.  
    6. class MoveListModel : public QStringListModel
    7. {
    8. Q_OBJECT
    9.  
    10. public:
    11. MoveListModel( QObject *parent ) : QStringListModel( parent ) {}
    12. ~MoveListModel() {}
    13.  
    14. virtual Qt::DropActions supportedDropActions() const override
    15. {
    16. return Qt::MoveAction;
    17. }
    18.  
    19. private:
    20.  
    21. };
    22. #endif // MOVELISTMODEL_H
    To copy to clipboard, switch view to plain text mode 

    With this code, if you make mpModelA a MoveListModel (and leave mpModelB alone), then if you select an item from list A and drag it to list B, it will be moved. However, because the default drop action Qt::CopyOption is not in these supportedDropActions flags, you cannot simply copy from B to A; on Windows, you must SHIFT-CLICK-DRAG from list B to move the item from list B to A. There is no longer any way to simply copy from B to A.

    If you change the flags to return both copy and move:
    Qt Code:
    1. virtual Qt::DropActions supportedDropActions() const override
    2. {
    3. return Qt::CopyAction | Qt::MoveAction;
    4. }
    To copy to clipboard, switch view to plain text mode 

    Now, the default CLICK-DRAG from list A to list B will move the item. A CTRL-CLICK-DRAG will copy the item. However, for list B (which is using the standard QStringListModel), the default CLICK-DRAG will copy the item from B to A, CTRL-CLICK-DRAG also copies the item, and SHIFT-CLICK-DRAG moves it.

    Clear as mud, right? :-)
    Last edited by d_stranz; 10th August 2020 at 00:48.
    <=== 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
    47
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QListView drag and drop

    This works, in the sense that the text strings can be moved and appear in the target view. But I have not been able to read out the data dropped on the target view. If I read it out like this
    Qt Code:
    1. QStringList listeB = mpModelB->stringList();
    To copy to clipboard, switch view to plain text mode 
    I get the original data in listeB, but not the data I dropped onto it. But since it is displayed by viewB it must have been added to the model? What am I missing here?

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

    Default Re: QListView drag and drop

    But since it is displayed by viewB it must have been added to the model? What am I missing here?
    It seems logical that it should be added to the model. According to the docs, the stringList() method should reflect what is currently in the model.

    I would suggest looking at the model's rowCount() value. If it changes with drags and drops, it should indicate that the model is being changed. You could also connect to the dataChanged() signal.
    <=== 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
    47
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: QListView drag and drop

    Quote Originally Posted by d_stranz View Post
    It seems logical that it should be added to the model. According to the docs, the stringList() method should reflect what is currently in the model.

    I would suggest looking at the model's rowCount() value. If it changes with drags and drops, it should indicate that the model is being changed. You could also connect to the dataChanged() signal.
    I finally got it working. Or almost. I subclassed QListView and reimplemented dropEvent. I did try that before as well, but it turned out I needed
    Qt Code:
    1. QListView::dropEvent(e);
    To copy to clipboard, switch view to plain text mode 
    Apparently, I was breaking the event loop. I than called rowCount() and stringList(), as you suggested, and now it works. I should perhaps mention that I also subclassed QStringListModel, as you suggested in a previous post, in order to get a move, rather than a copy action, which also works fine.

    There is a small hick-up, however, as the row count, and the data, of the source listview are not updated until I do a local move in the destination listview. As a test I put a pushbutton on the widget and read the row count and data manually and then I get the correct values. So it seems calling the read function from the dropEvent function is a bit too early. So I wonder if there is a way to detect that the move has been compleated?

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

    Default Re: QListView drag and drop

    So it seems calling the read function from the dropEvent function is a bit too early.
    When are you calling QListView::dropEvent()? At the start of your own event handler or at the end? It is possible that there could be other events that get queued up that can't be acted on until your own event handler exits. Try calling processEvents() after calling the QListView::dropEvent() to see if that helps.

    I am not really happy with that, though. It doesn't seem right that you should have to derive from QListView just to get the model updated. All of that should be built in - if it supports drops in the first place, it should be taking care of updating the model.

    Can you post some code from your original implementation where asking for the string list returns the wrong result?
    <=== 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.

  7. #7
    Join Date
    Jan 2020
    Posts
    47
    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 

  8. #8
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    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.

  9. #9
    Join Date
    Jan 2020
    Posts
    47
    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...

  10. #10
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    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.

  11. #11
    Join Date
    Jan 2020
    Posts
    47
    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.

  12. #12
    Join Date
    Jan 2020
    Posts
    47
    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?

  13. #13
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    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 23: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.

  14. #14
    Join Date
    Jan 2020
    Posts
    47
    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.

  15. #15
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    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.

  16. #16
    Join Date
    Jan 2020
    Posts
    47
    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, 10:13
  2. qt 3.3 qlistview drag and drop
    By harishiva in forum Qt Programming
    Replies: 0
    Last Post: 21st September 2011, 13:46
  3. Replies: 2
    Last Post: 13th October 2010, 22:51
  4. Drag and Drop from QListView to QGraphicsView
    By NoRulez in forum Qt Programming
    Replies: 0
    Last Post: 20th August 2009, 15:26
  5. Problem with drag&drop in qlistview
    By mambo in forum Qt Programming
    Replies: 2
    Last Post: 11th September 2006, 17: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.