QTableView拖放行无法正常工作

Posted

技术标签:

【中文标题】QTableView拖放行无法正常工作【英文标题】:QTableView drag & drop rows not working properly 【发布时间】:2019-07-19 05:50:12 【问题描述】:

我正在尝试移动 QTableView 的行。我根据this article构建了一个小的MVCE。我可以成功移动行,但是一旦我放下行就会出现奇怪的效果,请参见下面的打印屏幕:

创建了一个新的 row5,而不是将 two 推送到 Row0threeRow1

所以正确的结果应该是:

相反,我得到以下不正确的结果:

我一直在尝试通过阅读this source 来解决这个问题,这对于构建和测试模型很有用。除此之外,this 有助于理解提供给QTableView 的参数,但并没有完全解决问题。另外from here对该过程的简短描述有助于阅读,但我的问题仍然存在。最后我发现this interesting source 似乎将Qt::ItemFlags 描述为可能的解决方案,但无法正确地将其与可能的解决方案联系起来。

请看下面最重要的部分代码:

ma​​in.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();

    QListView *listView = new QListView;
    listView->setDragEnabled(true);
    listView->setAcceptDrops(true);
    listView->setModel(model);
    listView->setDefaultDropAction(Qt::MoveAction);
    listView->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;

非常感谢您指出正确的方向并试图阐明此事

【问题讨论】:

【参考方案1】:

NewModel::dropMimeData() 中,您将在指定位置插入行。你没有做的是:

    检查动作是 CopyAction 还是 MoveAction

对我来说,即使使用 view.setDefaultDropAction(Qt::MoveAction),我也总是会收到 CopyAction。到目前为止,我怀疑 QT5 中存在忽略默认放置操作的错误。

    对于 MoveAction,删除原始行

我不确定如何正确地从一个视图移动到另一个视图,或者在应用程序之间更难移动。但是当您在同一个表中移动时,您可以在 drop 方法中删除旧行。

实际上模型支持直接移动行而不是删除+插入。见https://doc.qt.io/qt-5/qabstractitemmodel.html#beginMoveRows

【讨论】:

以上是关于QTableView拖放行无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

可编辑的 QTableView 和 Pandas 无法正常工作

qt 中Qtable widget中如何加一个表

拖放到动态创建的 div 后,Jquery-ui 可拖动无法正常工作

QT - QTableView removeRow() 崩溃

QTableView + QAbstractTableModel:通过拖放移动行

将代码放置在 Joomla 文章中时,Jquery 拖放在 Chrome 浏览器中无法正常工作