Results 1 to 7 of 7

Thread: Inconsistent QListWidget drag&drop behaviour on Win and macOS

  1. #1
    Join Date
    Dec 2017
    Location
    Cesena, Italy
    Posts
    13
    Thanks
    4
    Thanked 1 Time in 1 Post
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Inconsistent QListWidget drag&drop behaviour on Win and macOS

    I'm developing an application for use on both windows (I'm using windows 10, Qt 5.12.4 and mingw 7.3.0 32bit) and mac (High Sierra, Qt 5.12.4 and Clang 64 bit).
    A part of the application consists of a QListWidget where I want to be able to move items up and down by dragging them.
    In addition I'd like to do a couple of other things:
    1) intercept these internal drag'n'drop events with a slot
    2) do operations on the items within the QListWidget in the window destructor before closing the application.

    The problem is that while everything works fine on win, on macos when I drag'n'drop the items, the item being moved disappears at the drop.
    I found that using the method setMovement(QListView::Snap) on the QListWidget fixes the drag'n'drop problem (items move fine also on mac), but completely breakes the rest of the behaviours: I cannot intercept the drag'n'drop events and when I try to access the items before closing the application I get a segfault.

    Does anyone know what am I doing wrong or if is there a reasonable workaround?

    Here's a small example to reproduce the issue:

    dnd.pro
    Qt Code:
    1. QT += core gui
    2.  
    3. greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    4.  
    5. CONFIG += c++11
    6.  
    7. # The following define makes your compiler emit warnings if you use
    8. # any Qt feature that has been marked deprecated (the exact warnings
    9. # depend on your compiler). Please consult the documentation of the
    10. # deprecated API in order to know how to port your code away from it.
    11. DEFINES += QT_DEPRECATED_WARNINGS
    12.  
    13. # You can also make your code fail to compile if it uses deprecated APIs.
    14. # In order to do so, uncomment the following line.
    15. # You can also select to disable deprecated APIs only up to a certain version of Qt.
    16. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
    17.  
    18. SOURCES += \
    19. main.cpp \
    20. mainwindow.cpp
    21.  
    22. HEADERS += \
    23. mainwindow.h
    24.  
    25. FORMS += \
    26. mainwindow.ui
    27.  
    28. # Default rules for deployment.
    29. qnx: target.path = /tmp/$${TARGET}/bin
    30. else: unix:!android: target.path = /opt/$${TARGET}/bin
    31. !isEmpty(target.path): INSTALLS += target
    To copy to clipboard, switch view to plain text mode 

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

    mainwindow.ui (default form for a Qt widgets application, did nothing in here)
    Qt Code:
    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <ui version="4.0">
    3. <class>MainWindow</class>
    4. <widget class="QMainWindow" name="MainWindow">
    5. <property name="geometry">
    6. <rect>
    7. <x>0</x>
    8. <y>0</y>
    9. <width>800</width>
    10. <height>600</height>
    11. </rect>
    12. </property>
    13. <property name="windowTitle">
    14. <string>MainWindow</string>
    15. </property>
    16. <widget class="QWidget" name="centralwidget"/>
    17. <widget class="QMenuBar" name="menubar"/>
    18. <widget class="QStatusBar" name="statusbar"/>
    19. </widget>
    20. <resources/>
    21. <connections/>
    22. </ui>
    To copy to clipboard, switch view to plain text mode 

    mainwindow.h
    Qt Code:
    1. #ifndef MAINWINDOW_H
    2. #define MAINWINDOW_H
    3.  
    4. #include <QMainWindow>
    5. #include <QListWidget>
    6.  
    7. QT_BEGIN_NAMESPACE
    8. namespace Ui { class MainWindow; }
    9. QT_END_NAMESPACE
    10.  
    11. class MainWindow : public QMainWindow
    12. {
    13. Q_OBJECT
    14.  
    15. public:
    16. MainWindow(QWidget *parent = nullptr);
    17. ~MainWindow();
    18.  
    19. private:
    20. Ui::MainWindow *ui;
    21. QVector <QListWidgetItem *> items;
    22. };
    23. #endif // MAINWINDOW_H
    To copy to clipboard, switch view to plain text mode 

    mainwindow.cpp
    Qt Code:
    1. #include "mainwindow.h"
    2. #include "ui_mainwindow.h"
    3.  
    4. #include <QBoxLayout>
    5. #include <QDebug>
    6.  
    7. MainWindow::MainWindow(QWidget *parent)
    8. : QMainWindow(parent)
    9. , ui(new Ui::MainWindow)
    10. {
    11. ui->setupUi(this);
    12.  
    13. QWidget * w = new QWidget;
    14. this->setCentralWidget(w);
    15.  
    16. w->setLayout(vl);
    17.  
    18. this->layout()->addWidget(lw);
    19.  
    20. lw->setDragDropMode(QAbstractItemView::InternalMove);
    21. lw->setDefaultDropAction(Qt::MoveAction);
    22. lw->setMovement(QListView::Snap); // when this line is commented drag'n'drop does not work on mac, when it is uncommented features 1) and 2) do not work on both win and mac
    23.  
    24. items.append(new QListWidgetItem("item 1"));
    25. items.append(new QListWidgetItem("item 2"));
    26. items.append(new QListWidgetItem("item 3"));
    27. lw->addItem(items[0]);
    28. lw->addItem(items[1]);
    29. lw->addItem(items[2]);
    30.  
    31. // feature 1) intercept drag'n'drop events
    32. QAbstractItemModel * model = lw->model();
    33. connect(model, &QAbstractItemModel::rowsMoved, this, [=] (const QModelIndex &, int from, int, const QModelIndex &, int to)
    34. {
    35. qDebug() << "moved item from" << from << "to" << to;
    36. });
    37. }
    38.  
    39. MainWindow::~MainWindow()
    40. {
    41. // feature 2) operate on items before closing the application
    42. for (int i = 0; i < items.size(); i++)
    43. {
    44. qDebug() << items[i]->text();
    45. }
    46. delete ui;
    47. }
    To copy to clipboard, switch view to plain text mode 

    Thanks a lot for any input!

  2. #2
    Join Date
    Mar 2009
    Location
    Brisbane, Australia
    Posts
    7,729
    Thanks
    13
    Thanked 1,610 Times in 1,537 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows
    Wiki edits
    17

    Default Re: Inconsistent QListWidget drag&drop behaviour on Win and macOS

    I do not have a Mac, but your code also segfaults on exit with Linux 64-bit Qt 5.13.2.

    When I run it from the command line i see this warning:
    Qt Code:
    1. QMainWindowLayout::addItem: Please use the public QMainWindow API instead
    To copy to clipboard, switch view to plain text mode 
    In this code:
    Qt Code:
    1. this->layout()->addWidget(lw);
    To copy to clipboard, switch view to plain text mode 
    you add the list widget to the layout of the main window not the layout of the central widget you just created. When I add it to the central widget the segfault issue disappears.
    Qt Code:
    1. vl->addWidget(lw);
    To copy to clipboard, switch view to plain text mode 


    The items disappear when dropped outside the list widget, e.g. on the title bar of the main window. Items dragged within the list widget seem to behave as expected.

  3. #3
    Join Date
    Dec 2017
    Location
    Cesena, Italy
    Posts
    13
    Thanks
    4
    Thanked 1 Time in 1 Post
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Re: Inconsistent QListWidget drag&drop behaviour on Win and macOS

    Thanks ChrisW67 for replying!
    Sorry for the wrong layout usage, but I put together the minimum code a bit in a hurry before leaving the office for some holidays.
    Have you checked if the parts of code identified as features 1) and 2) work on your system?

    Best

  4. #4
    Join Date
    Mar 2009
    Location
    Brisbane, Australia
    Posts
    7,729
    Thanks
    13
    Thanked 1,610 Times in 1,537 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows
    Wiki edits
    17

    Default Re: Inconsistent QListWidget drag&drop behaviour on Win and macOS

    The corrections I made in #2 to your code from #1 make the crash-on-exit disappear and reordering work correctly for me.
    Dragging an item off the list widget removes it completely either way.

  5. #5
    Join Date
    Dec 2017
    Location
    Cesena, Italy
    Posts
    13
    Thanks
    4
    Thanked 1 Time in 1 Post
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Re: Inconsistent QListWidget drag&drop behaviour on Win and macOS

    I understand that the correction you suggested corrects the crash on exit, but that's not my problem (it was an error I introduced in the minimum project example and does not affect my full project). The problem is that if I comment this line
    Qt Code:
    1. lw->setMovement(QListView::Snap); // when this line is commented drag'n'drop does not work on mac, when it is uncommented features 1) and 2) do not work on both win and mac
    To copy to clipboard, switch view to plain text mode 
    the drag'n'drop works only on win, while if I uncomment it the drag'n'drop works also on mac, but the two other features described in the original post
    1) intercept these internal drag'n'drop events with a slot
    2) do operations on the items within the QListWidget in the window destructor before closing the application.
    implemented with the following lines of code
    Qt Code:
    1. // feature 1) intercept drag'n'drop events
    2. QAbstractItemModel * model = lw->model();
    3. connect(model, &QAbstractItemModel::rowsMoved, this, [=] (const QModelIndex &, int from, int, const QModelIndex &, int to)
    4. {
    5. qDebug() << "moved item from" << from << "to" << to;
    6. });
    To copy to clipboard, switch view to plain text mode 
    and
    Qt Code:
    1. // feature 2) operate on items before closing the application
    2. for (int i = 0; i < items.size(); i++)
    3. {
    4. qDebug() << items[i]->text();
    5. }
    To copy to clipboard, switch view to plain text mode 
    cease to work.
    Is there anything I can do to get everything working, i.e. reading "moved item from # to #" when an item is moved and reading the list of items' text when I close the application?

  6. #6
    Join Date
    Mar 2009
    Location
    Brisbane, Australia
    Posts
    7,729
    Thanks
    13
    Thanked 1,610 Times in 1,537 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows
    Wiki edits
    17

    Default Re: Inconsistent QListWidget drag&drop behaviour on Win and macOS

    1) intercept these internal drag'n'drop events with a slot
    Calling setMovement() with Free or Snap interferes with signals on Linux also (Static is the default). I see no signals but visually and internally the DND works.
    I assume you are making this call to try to get DND working on Mac (you say it is not).
    What happens if you call only setDragDropMode() and not setDefaultDropAction() or setMovement()


    2) do operations on the items within the QListWidget in the window destructor before closing the application.
    You create, and cache pointers to, three list items.
    When these are added to the list widget the widget takes ownership.
    When the setMovement() is called with Free or Snap I notice that drag and drop actions cause the widget (and internal model) to delete and create new items. This invalidates the cached pointers to the item(s) concerned. When you access the items through your cached pointers you risk a seg fault. I suggest:
    • Dispense with the items vector
    • Keep a pointer to the list widget
    • Create and add the items directly to the list widget
    • Access the items in the widget using the widget's API:
      Qt Code:
      1. for (int i = 0; i < lw->count(); ++i) {
      2. qDebug() << i << lw->item(i)->text();
      3. }
      To copy to clipboard, switch view to plain text mode 

  7. The following user says thank you to ChrisW67 for this useful post:

    fcona (7th January 2020)

  8. #7
    Join Date
    Dec 2017
    Location
    Cesena, Italy
    Posts
    13
    Thanks
    4
    Thanked 1 Time in 1 Post
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Re: Inconsistent QListWidget drag&drop behaviour on Win and macOS

    What happens if you call only setDragDropMode() and not setDefaultDropAction() or setMovement()
    It seems this was enough to get everything working!
    That is, to get the behaviour I want I just need to call setDragDropMopde(QAbstractItemView::InternalMove). Any call to setDefaultDropAction() or setMovement() interferes somehow. I got confused cause on Win the call to setDefaultDropAction() does not change the overall behaviour (at least nothing I could notice), while on Mac it breaks the d'n'd, making items disappear whenever they are moved.
    I also implemented the further suggested fix (no items vector and calls to QListWidget::item).
    Here's for future reference the full mainwindow.cpp that works for me
    Qt Code:
    1. #include "mainwindow.h"
    2. #include "ui_mainwindow.h"
    3.  
    4. #include <QBoxLayout>
    5. #include <QDebug>
    6.  
    7. MainWindow::MainWindow(QWidget *parent)
    8. : QMainWindow(parent)
    9. , ui(new Ui::MainWindow)
    10. {
    11. ui->setupUi(this);
    12.  
    13. QWidget * w = new QWidget;
    14. this->setCentralWidget(w);
    15.  
    16. w->setLayout(vl);
    17.  
    18. lw = new QListWidget;
    19. vl->layout()->addWidget(lw);
    20.  
    21. lw->setDragDropMode(QAbstractItemView::InternalMove);
    22. // lw->setDefaultDropAction(Qt::MoveAction); REMOVED
    23. // lw->setMovement(QListView::Snap); REMOVED
    24.  
    25. lw->addItem(new QListWidgetItem("item 1"));
    26. lw->addItem(new QListWidgetItem("item 2"));
    27. lw->addItem(new QListWidgetItem("item 3"));
    28.  
    29. QAbstractItemModel * model = lw->model();
    30. connect(model, &QAbstractItemModel::rowsMoved, this, [=] (const QModelIndex &, int from, int, const QModelIndex &, int to)
    31. {
    32. qDebug() << "moved item from" << from << "to" << to;
    33. });
    34. }
    35.  
    36. MainWindow::~MainWindow()
    37. {
    38. for (int i = 0; i < lw->count(); i++)
    39. {
    40. qDebug() << lw->item(i)->text();
    41. }
    42. delete ui;
    43. }
    To copy to clipboard, switch view to plain text mode 
    and of course you need this declaration too in MainWindow's properties:
    Qt Code:
    To copy to clipboard, switch view to plain text mode 
    Thank you very much for your help!

  9. The following user says thank you to fcona for this useful post:

    d_stranz (7th January 2020)

Similar Threads

  1. Replies: 0
    Last Post: 28th June 2019, 13:36
  2. Replies: 0
    Last Post: 13th December 2016, 13:22
  3. Replies: 2
    Last Post: 30th January 2014, 07:46
  4. Replies: 0
    Last Post: 8th December 2011, 13:21
  5. Replies: 3
    Last Post: 10th June 2010, 16:13

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.