QTableView拖拽交换行、列、单元格

Qt Model-View 设置 QHeaderView 的 setSectionsMovable 为 true 后可以拖拽表头移动行、列位置。但有时也需要拽拽内容区域进行交换(也有和组件外进行拖拽交互的,本文不涉及)。要完成这个功能,除了对 View 进行几个设置,重头戏在于 Model 的定制。无论是继承 QAbstractTableModel ,或是 QStandardItemModel 都是可以实现拖拽交换的,在我的 Demo 中两个都进行了测试。不过我的 Demo 只做了 单元格交换,可以自行修改为行、列交换,只需要 View 设置下 setSelectionBehavior ,然后在 Model 中交换时交换整行、整列。(如果只想移动而不是交换行列的话,可以 takeItem ,然后根据 drop 时坐标进行插入)

效果图 GIF:

QTableView拖拽交换行、列、单元格_第1张图片

首先是 QTableView 的设置:

    view->setSelectionMode(QAbstractItemView::SingleSelection); //不是必要的
    //可以配合行/列选中,需要在Model中做相应处理
    //view->setSelectionBehavior(QAbstractItemView::SelectRows);
    view->setDragEnabled(true);
    view->setDefaultDropAction(Qt::MoveAction); //不是必要的
    view->setDragDropMode(QAbstractItemView::InternalMove);

接下来需要对 Model 额外实现 4 个接口:

    // 允许的操作,加上drag drop
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    // 允许move
    Qt::DropActions supportedDropActions() const override;
    // drag时携带的信息
    QMimeData *mimeData(const QModelIndexList &indexes) const override;
    // drop时根据drag携带的信息进行处理
    bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;

flags 和 supportedDropActions 是为了支持拖拽功能,mimeData 则用来处理拖拽时对应的信息,比如我们可以把行列信息放进去,在 drop 时对两个 index 的数据进行交换。下面是我在 QStandardItemModel 派生类中的实现:


Qt::ItemFlags MyStandardItemModel::flags(const QModelIndex &index) const
{
    if (index.isValid())
        return Qt::ItemIsDragEnabled  | Qt::ItemIsDropEnabled  | QStandardItemModel::flags(index);
    return QStandardItemModel::flags(index);
}

Qt::DropActions MyStandardItemModel::supportedDropActions() const
{
    return Qt::MoveAction | QStandardItemModel::supportedDropActions();
}

QMimeData *MyStandardItemModel::mimeData(const QModelIndexList &indexes) const
{
    QMimeData *data=QStandardItemModel::mimeData(indexes);
    if(data){
        // parent mimeData中已判断indexes有效性,无效的会返回nullptr
        // 也可以把信息放到model的mutable成员中
        data->setData("row",QByteArray::number(indexes.at(0).row()));
        data->setData("col",QByteArray::number(indexes.at(0).column()));
    }
    return data;
}

bool MyStandardItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    if(!data||action!=Qt::MoveAction)
        return false;

    //这里没有判断toint ok(数据转换有效性)
    const QModelIndex old_index=index(data->data("row").toInt(),
                                      data->data("col").toInt());
    const QModelIndex current_index=parent;
    //可以先对index有效性进行判断,无效返回false,此处略过
    QStandardItem *old_item=takeItem(old_index.row(),old_index.column());
    QStandardItem *current_item=takeItem(current_index.row(),current_index.column());
    //交换两个item
    setItem(old_index.row(),old_index.column(),current_item);
    setItem(current_index.row(),current_index.column(),old_item);
    return true;
}

(不用担心 QMimeData 没释放,貌似它是通过对应 event 对象一并释放的) 

参考 Qt 文档:https://doc.qt.io/qt-5/model-view-programming.html

完整代码链接 GitHub:https://github.com/gongjianbo/MyTestCode/tree/master/Qt/QTableViewMoveAction

完整代码链接 CSDN:https://download.csdn.net/download/gongjianbo1992/12547136

你可能感兴趣的:(Qt,略知一二,QTableView,DragDrop)