lundi 25 février 2019

QTableView drag & drop rows not working properly

I am trying to move rows of a QTableView. I build a small MVCE according to this article. I can successfully move rows, however a strange effect happens as soon as I drop the row, see below print screen:

dragDrop_error

A new row5 gets created instead of pushing two to Row0 and three to Row1

So the correct result should be:

Correct

instead I get the following incorrect result:

Incorrect

I have been trying to solve this problem reading this source, which was useful to build and test the model. In addition to that this was useful to understand the parameters to give to the QTableView but did not totally solved the problem. Also from here a small description of the process was useful to read but still my problem persists. Finally I found this interesting source that seems to describe to address Qt::ItemFlags as possible solution but could not properly relate it to a possible solution.

See below the most important part of the code:

main.cpp

#include <QApplication>
#include <QtGui>
#include <QAbstractItemModel>
#include <QTableView>
#include <QListView>
#include <QAbstractItemView>
#include "newmodel.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QStringList numbers;
    numbers << "one" << "two" << "three" << "four" << "five";
    QAbstractItemModel *model = new NewModel(numbers);
    QTableView *tableView = new QTableView;
    tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
    tableView->dragDropOverwriteMode();
    tableView->setDragEnabled(true);
    tableView->setAcceptDrops(true);
    tableView->setDropIndicatorShown(true);
    tableView->setModel(model);
    tableView->setDefaultDropAction(Qt::MoveAction);
    tableView->show();
    return a.exec();
}

newmodel.cpp

#include "newmodel.h"
#include <QStringListModel>
#include <QDebug>

NewModel::NewModel(const QStringList &strings, QObject *parent)
    : QAbstractListModel(parent)
    , stringList(strings)
{}

int NewModel::rowCount(const QModelIndex &parent) const
{
    return stringList.count();
    Q_UNUSED(parent);
}

QVariant NewModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();
    if(index.row() >= stringList.size())
        return QVariant();
    if(role == Qt::DisplayRole || role == Qt::EditRole)
        return stringList.at(index.row());
    else
        return QVariant();
}

QVariant NewModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(role != Qt::DisplayRole)
        return QVariant();
    if(orientation == Qt::Horizontal)
        return QString("Column %1").arg(section);
    else
        return QString("Row %1").arg(section);
}

Qt::ItemFlags NewModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);

    if(index.isValid())
        return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
    else
        return Qt::ItemIsDropEnabled | defaultFlags;
}

bool NewModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(index.isValid() && role == Qt::EditRole) {
        stringList.replace(index.row(), value.toString());
        emit dataChanged(index, index);
        return true;
    }
    return false;
}

bool NewModel::insertRows(int position, int rows, const QModelIndex &parent)
{
    beginInsertRows(QModelIndex(), position, position+rows-1);
    for(int row = 0; row < rows; row++) {
        stringList.insert(position, "");
    }
    endInsertRows();
    return true;
    Q_UNUSED(parent);
}

bool NewModel::removeRows(int position, int rows, const QModelIndex &parent)
{
    beginRemoveRows(QModelIndex(), position, position+rows-1);
    for(int row = 0; row < rows; ++row) {
        stringList.removeAt(position);
    }
    endRemoveRows();
    return true;
    Q_UNUSED(parent);
}

Qt::DropActions NewModel::supportedDropActions() const
{
    return Qt::CopyAction | Qt::MoveAction;
}

QStringList NewModel::mimeTypes() const
{
    QStringList types;
    types << "application/vnd.text.list";
    return types;
}

QMimeData *NewModel::mimeData(const QModelIndexList &indexes) const
{
    QMimeData *mimeData = new QMimeData();
    QByteArray encodedData;
    QDataStream stream(&encodedData, QIODevice::WriteOnly);
    foreach(const QModelIndex &index, indexes) {
        if(index.isValid()) {
            QString text = data(index, Qt::DisplayRole).toString();
            stream << text;
        }
    }
    mimeData->setData("application/vnd.text.list", encodedData);
    return mimeData;
}

bool NewModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
                                   int row, int column, const QModelIndex &parent)
{
    qDebug() << action;
    if(action == Qt::IgnoreAction)
        return true;
    if(!data->hasFormat("application/vnd.text.list"))
        return false;
    if(column > 0)
        return false;
    int beginRow;
    if(row != -1)
        beginRow = row;
    else if(parent.isValid())
        beginRow = parent.row();
    else
        beginRow = rowCount(QModelIndex());
    QByteArray encodedData = data->data("application/vnd.text.list");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);
    QStringList newItems;
    int rows = 0;
    while(!stream.atEnd()) {
        QString text;
        stream >> text;
        newItems << text;
        ++rows;
    }
    insertRows(beginRow, rows, QModelIndex());
    foreach(const QString &text, newItems) {
        QModelIndex idx = index(beginRow, 0, QModelIndex());
        setData(idx, text);
        beginRow++;
    }
    return true;
}

bool NewModel::dragDropOverwtiteMode() const
{
    return false;
}

Thank you very much for pointing in the right direction and trying to shed light on this matter

Aucun commentaire:

Enregistrer un commentaire