Walletfox.com

Reorderable list in Qt with QListWidget or QListView

This article shows how to create a reorderable list in Qt.


Two variants of the solution are presented:

  • Variant 1 - item reordering within QListWidget.
  • Variant 2 - item reordering within QListView and a subclassed QStringListModel. This approach requires that we override methods flags().

Variant 1 - QListWidget

This variant shows how to reorder QListWidget. The header customdialog.h can be seen below.

class CustomDialog : public QDialog
{
    Q_OBJECT
public:
    CustomDialog(QWidget *parent = Q_NULLPTR);
public slots:
    void save();
private:
    QListWidget* widget;
    QDialogButtonBox* buttonBox;
    QGroupBox* viewBox;
    QPushButton* saveButton;
    QPushButton* closeButton;

    void createListWidget();
    void createOtherWidgets();
    void createLayout();
    void createConnections();
};

The method createListWidget() can be seen below. We populate the list widget with the method addItems(QStringList). Once the items have been created, we set the drag/drop mode to QAbstractItemView::InternalMove.

void CustomDialog::createListWidget(){
    widget = new QListWidget;
    QStringList strList;
    strList << "bubble sort" << "quicksort" << "insertion sort" << "merge sort"
            << "shell sort" << "bucket sort" << "radix sort" << "selection sort";

    widget->addItems(strList);
    widget->setDragDropMode(QAbstractItemView::InternalMove);
}

This is all that is necessary to implement reordering of items within QListWidget. The entire implementation can be found in the source files above (Variant 1).

Variant 2 - QListView and subclassed QStringListModel

Unlike the previous implementation, this implementation employs QListView and subclasses QStringListModel. Below is the header customdialog.h, which stores a pointer to an instance of QListView and a custom model instance CustonListModel.

class CustomDialog : public QDialog
{
    Q_OBJECT
public:
    CustomDialog(QWidget *parent = Q_NULLPTR);
public slots:
    void save();
private:
    CustomListModel* model;
    QListView* view;
    QDialogButtonBox* buttonBox;
    QGroupBox* viewBox;
    QPushButton* saveButton;
    QPushButton* closeButton;

    void createListModelView();
    void createOtherWidgets();
    void createLayout();
    void createConnections();
};

In the constructor of CustomDialog we instantiate QListView and CustomListModel. After that we call the method createListModelView() to populate the list model with data. Just like for the previous case, we setDragDropMode of the view to QAbstractItemView::InternalMove.

CustomDialog::CustomDialog(QWidget *parent): QDialog(parent)
{
    setWindowTitle(tr("Reorderable QListView"));

    createListModelView();
    createOtherWidgets();
    createLayout();
    createConnections();
}
void CustomDialog::createListModelView(){
    view = new QListView;
    model = new CustomListModel;
    view->setModel(model);
    view->setDragDropMode(QAbstractItemView::InternalMove);

    QStringList strList;
    strList << "bubble sort" << "quicksort" << "insertion sort" << "merge sort"
            << "shell sort" << "bucket sort" << "radix sort" << "selection sort";
    model->setStringList(strList);
}

Subclassing QStringListModel

To make items reorderable, we subclass the QStringListModel and override the method flags(). We have to make the items enabled, selectable, drag-enabled but not drop-enabled. The explanation for why we have to do this can be found below.

class CustomListModel : public QStringListModel
{
public:
    CustomListModel(QObject* parent = Q_NULLPTR);
    CustomListModel(const QStringList & strings, QObject* parent = Q_NULLPTR);
    Qt::ItemFlags flags (const QModelIndex& index) const;
    void save();
};
Qt::ItemFlags CustomListModel::flags (const QModelIndex & index) const {
    Qt::ItemFlags defaultFlags = QStringListModel::flags(index);
    if (index.isValid()){
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
    }
    return defaultFlags;
}
Note: The explanation for why we need to override flags() can be found in the documentation of the method bool dragDropOverwriteMode() const. The documentation says that the default behaviour when moving items is items' removal. To prevent the removal of the items it is necessary to reimplement flags() in such way that we do not return Qt::ItemIsDropEnabled.

That's it! Further details of the implementation can be found in the source files at the beginning of this article (Variant 2).

Tagged: Qt