如何实现QTableView拖拽调整行顺序并将更改同步到QSqlTableModel
解决Qt 5.15下QTableView拖拽调整行顺序并同步QSqlTableModel的方案
问题原因分析
- 直接配置QTableView拖拽属性不生效:默认的QSqlTableModel没有返回
Qt::ItemIsDragEnabled、Qt::ItemIsDropEnabled标记,视图层面不允许行拖拽操作 - 垂直表头拖拽不同步:垂直表头的
setSectionsMovable仅修改视图层的视觉展示顺序,不会修改底层模型的数据排序逻辑,模型刷新后就会恢复原始顺序
推荐实现方案(基于现有垂直表头拖拽改造,改动最小)
前置准备
你的数据库表需要新增一个INT类型的排序字段,例如命名为sort_seq,初始化时按现有行顺序依次赋值1、2、3...,用来持久化存储行的排序权重。
基础配置
保留你原来的垂直表头拖拽配置,额外加排序配置:
// 原有基础配置 ui->table_view->setSelectionMode(QAbstractItemView::SingleSelection); ui->table_view->setSelectionBehavior(QAbstractItemView::SelectRows); ui->table_view->setEditTriggers(QAbstractItemView::NoEditTriggers); // 原有垂直表头拖拽配置 ui->table_view->verticalHeader()->setSectionsMovable(true); ui->table_view->verticalHeader()->setDragEnabled(true); ui->table_view->verticalHeader()->setDragDropMode(QAbstractItemView::InternalMove); ui->table_view->verticalHeader()->setAcceptDrops(true); // 新增配置:禁用表头自带排序,避免和我们自定义排序冲突 ui->table_view->verticalHeader()->setSortIndicatorShown(false); // 模型配置:按sort_seq字段升序排序 int sortCol = model->fieldIndex("sort_seq"); model->setSort(sortCol, Qt::AscendingOrder); model->select();
同步逻辑实现
绑定垂直表头的sectionMoved信号,在拖拽结束后自动同步排序到底层模型和数据库:
connect(ui->table_view->verticalHeader(), &QHeaderView::sectionMoved, this, [=](int logicalIndex, int oldVisualIndex, int newVisualIndex){ // 临时屏蔽信号,避免修改模型时循环触发视图更新 ui->table_view->verticalHeader()->blockSignals(true); int sortCol = model->fieldIndex("sort_seq"); if (newVisualIndex > oldVisualIndex) { // 向下拖动:中间行排序值全部减1 for (int i = oldVisualIndex; i < newVisualIndex; ++i) { int curSeq = model->index(i, sortCol).data().toInt(); model->setData(model->index(i, sortCol), curSeq - 1); } // 设置被拖动行的新排序值 model->setData(model->index(oldVisualIndex, sortCol), newVisualIndex + 1); } else { // 向上拖动:中间行排序值全部加1 for (int i = newVisualIndex; i < oldVisualIndex; ++i) { int curSeq = model->index(i, sortCol).data().toInt(); model->setData(model->index(i, sortCol), curSeq + 1); } // 设置被拖动行的新排序值 model->setData(model->index(oldVisualIndex, sortCol), newVisualIndex + 1); } // 提交修改到数据库 model->submitAll(); // 刷新模型同步视图显示 model->select(); // 恢复信号接收 ui->table_view->verticalHeader()->blockSignals(false); });
可选方案:直接拖拽行调整顺序(无需拖表头)
如果需要点击行任意位置拖拽调整,需要自定义QSqlTableModel子类,开放拖拽权限并处理drop逻辑:
自定义模型代码
class CustomSqlModel : public QSqlTableModel { Q_OBJECT public: CustomSqlModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase()) : QSqlTableModel(parent, db) {} // 开放拖拽、放置权限 Qt::ItemFlags flags(const QModelIndex &index) const override { Qt::ItemFlags defaultFlags = QSqlTableModel::flags(index); if (index.isValid()) return defaultFlags | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; return defaultFlags | Qt::ItemIsDropEnabled; } QStringList mimeTypes() const override { return {"application/x-qabstractitemmodeldatalist"}; } Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; } bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override { if (action == Qt::IgnoreAction) return true; if (!data->hasFormat("application/x-qabstractitemmodeldatalist") || column > 0) return false; // 解析源行索引 int sourceRow; QByteArray encoded = data->data("application/x-qabstractitemmodeldatalist"); QDataStream stream(&encoded, QIODevice::ReadOnly); stream >> sourceRow; // 计算目标行索引 int destRow = parent.isValid() ? parent.row() : row; if (destRow < 0) destRow = rowCount() - 1; if (destRow == sourceRow) return true; // 这里复用上面的排序字段调整逻辑,修改对应行的sort_seq值后提交刷新即可 int sortCol = fieldIndex("sort_seq"); // 排序值调整逻辑和上面的sectionMoved槽函数完全一致,此处不再重复 submitAll(); select(); return true; } };
视图配置
ui->table_view->setDragEnabled(true); ui->table_view->setAcceptDrops(true); ui->table_view->setDropIndicatorShown(true); ui->table_view->setDragDropMode(QAbstractItemView::InternalMove);
内容的提问来源于stack exchange,提问作者Ginakira
相关产品推荐
相关产品推荐

