QTableView自定义拖拽行

3 篇文章 0 订阅

实现QTableView的行拖拽,支持多行选择拖拽,也可根据需要稍作修改实现QTreeView、QListView等的拖拽效果。

拖拽类实现

继承 QTableView 实现几个拖拽相关的方法
TableView.h

#ifndef TableView_H
#define TableView_H

#include <QObject>
#include <QListView>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QDragLeaveEvent>
#include <QStandardItemModel>
#include <QStandardItem>
#include <QMimeData>
#include <QDrag>
#include <QDebug>
#include <QTableView>

class TableView : public QTableView
{
    Q_OBJECT

public:
    explicit TableView(QWidget *parent = nullptr);

    bool isDraging() const {return IsDraging;}
    int offset() const {return 12;}
    int highlightedRow() const {return curHighlightedRow;}
    int dragRow() const {return curDragRow;}
    static QString myMimeType() { return QStringLiteral("TableView/text-icon"); }
private:
    //dropIndicator显示与消失
    void updateDropIndicator(int row);
protected:
    void dragEnterEvent(QDragEnterEvent *event) override;
    void dragLeaveEvent(QDragLeaveEvent *event) override;
    void dragMoveEvent(QDragMoveEvent *event) override;
    void dropEvent(QDropEvent *event) override;
    void startDrag(Qt::DropActions supportedActions) override;

private:
    bool IsDraging = false;
    //当前鼠标所在位置 行号
    int curHighlightedRow = -2;
    //被拖拽的行所在的位置
    int oldHighlightedRow = -2;
    //拖拽的行
    int curDragRow = -1;
    //当前鼠标放下所在行
    int curInsertRow = -1;
    //需要删除的旧行(行号)
    QList<int> oldSelectRow;
    //选中的拖拽行
    QList<int> curDragRows;
};

bool compareBarData(const int &barAmount1, const int &barAmount2);

#endif // TableView_H

TableView.cpp

#include "TableView.h"

TableView::TableView(QWidget *parent) :
    QTableView(parent)
{
    setDragEnabled(true);
    setAcceptDrops(true);
    setEditTriggers(QAbstractItemView::NoEditTriggers);

    this->setStyleSheet(
        "QListView::Item{height:20px;}"
    );
    //列撑满整个表格
    this->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    //去掉表格边框线
    this->setShowGrid(false);
    //整行选择
    this->setSelectionBehavior(QAbstractItemView::SelectRows);
    //按住ctrl多选
    this->setSelectionMode(QAbstractItemView::ExtendedSelection);
}

void TableView::dragEnterEvent(QDragEnterEvent *event)
{
    TableView *source = qobject_cast<TableView *>(event->source());
    if (source && source == this) {
        IsDraging = true;  //IsDraging(标志位)判断是否正在拖拽
        event->setDropAction(Qt::MoveAction);
        event->accept();
    }
}

void TableView::dragLeaveEvent(QDragLeaveEvent *event)
{
    oldHighlightedRow = curHighlightedRow;
    curHighlightedRow = -2;
    //刷新以使dropIndicator消失,
    updateDropIndicator(oldHighlightedRow);
    IsDraging = false;
    curInsertRow = -1;
    event->accept();
}
//dropIndicator显示与消失
void TableView::updateDropIndicator(int row)
{
    //这里用的是update(QModelIndex),这里也可以使用update(QRect),在表格所有列绘制分隔符
    for (int updateColI = 0; updateColI < this->model()->columnCount();updateColI++) {
        //刷新新区域使dropIndicator显示
        update(model()->index(row, updateColI));
        update(model()->index(row + 1, updateColI));
    }
}
void TableView::dragMoveEvent(QDragMoveEvent *event)
{
    TableView *source = qobject_cast<TableView *>(event->source());
    if (source && source == this) {
        oldHighlightedRow = curHighlightedRow;
        curHighlightedRow = indexAt(event->pos() - QPoint(0, offset())).row();

        //offset() = 9 = rowHeight / 2 - 1,其中 rowHeight 是行高
        if(event->pos().y() >= offset()){
            if(oldHighlightedRow != curHighlightedRow){
                //刷新以使dropIndicator消失
                updateDropIndicator(oldHighlightedRow);

                //刷新新区域使dropIndicator显示
                updateDropIndicator(curHighlightedRow);

            }else{
                //刷新新区域使dropIndicator显示
                updateDropIndicator(curHighlightedRow);
            }
            curInsertRow = curHighlightedRow + 1;
        }else{
            curHighlightedRow = -1;
            //刷新新区域使dropIndicator显示,第一行
            updateDropIndicator(0);

            curInsertRow = 0;
        }

        event->setDropAction(Qt::MoveAction);
        event->accept();
    }
}

void TableView::dropEvent(QDropEvent *event)
{
    TableView *source = qobject_cast<TableView *>(event->source());
    if (source && source == this){

        IsDraging = false;  //完成拖拽
        oldHighlightedRow = curHighlightedRow;
        curHighlightedRow = -2;

        //刷新以使dropIndicator消失
        updateDropIndicator(oldHighlightedRow);
        
        //这里我像QListWidget那样调用父类dropEvent(event)发现不起作用(原因尚不明),没办法,只能删除旧行,插入新行
        //从event->mimeData()取出拖拽数据
        QList<int> text;
        //                QIcon icon;
        QByteArray itemData = event->mimeData()->data(myMimeType());
        QDataStream dataStream(&itemData, QIODevice::ReadOnly);
        dataStream >> text;// >> icon;

        //[1]

        for (int rowI = 0;rowI < text.size();rowI++) {
            if(curInsertRow == text.at(rowI) || curInsertRow == text.at(rowI) + 1)
            {
                curDragRows.clear();
                return;
            }
        }

        qSort(text.begin(),text.end(),compareBarData);
        QList<QList<QStandardItem*>> standardItemRow;

        for (int rowI = 0;rowI < text.size();rowI++) {
            QList<QStandardItem*> standardItem;
            for (int i = 0;i < this->model()->columnCount();i++) {
                QModelIndex index = this->model()->index(text.at(rowI),i);
                QString name = this->model()->data(index).toString();
                standardItem.append(new QStandardItem(name));
                qDebug()<< "[" << __LINE__ << "-" << __FUNCTION__ << "]"<<name;
            }
            standardItemRow.append(standardItem);
        }

        for (int rowI = 0;rowI < text.size();rowI++) {
            model()->insertRow(curInsertRow+rowI);  //插入新行
            QStandardItemModel *listModel = qobject_cast<QStandardItemModel *>(model());
            for (int i = 0;i < this->model()->columnCount();i++) {
                listModel->setItem(curInsertRow+rowI, i, standardItemRow[rowI][i]);
            }
        }
        setCurrentIndex(model()->index(curInsertRow, 0));  //插入行保持选中状态
        event->setDropAction(Qt::MoveAction);
        event->accept();
    }
}

/**!使用startDrag()则不需要判断拖拽距离,按下鼠标时触发
*/
void TableView::startDrag(Qt::DropActions)
{	
	//清空当前拖拽列表,防止重复拖拽选中
	curDragRows.clear();
	//当前拖拽行
    curDragRow = currentIndex().row();
    QStandardItemModel *listModel = qobject_cast<QStandardItemModel *>(model());
    QModelIndexList modelIndexList = this->selectionModel()->selectedRows();
    // 放入QMimeData容器中的拖拽数据(选中的行索引)
    QList<int> text;
    foreach (QModelIndex modelIndex, modelIndexList) {
    	//把当前拖拽数据放到拖拽列表中,实现后面拖拽完成删除旧的行
        curDragRows.append(modelIndex.row());
        //把拖拽数据的行索引放在QMimeData容器中
        text.append(modelIndex.row());
    }
    QByteArray itemData;
    QDataStream dataStream(&itemData, QIODevice::WriteOnly);
    dataStream << text;

	//设置拖拽对象,可自动定义拖拽的缩略图样式可自己继承 QWidget 画一个出来
	/**QWidget* widget= new QWidget(this);
	widget->setupthumbnail(icon, text);
	QPixmap pixmap = widget->grab();
	drag->setPixmap(pixmap);
	drag->setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2));
	*/
    QMimeData *mimeData = new QMimeData;
    mimeData->setData(myMimeType(), itemData);
    QDrag *drag = new QDrag(this);
    drag->setMimeData(mimeData);
    
    //删除的行需要根据curInsertRow和curDragRow的大小关系来判断,大于当前放下的行需要加上放下的行数,小于当前放下的行则不需加
    if(drag->exec(Qt::MoveAction) == Qt::MoveAction){
        int sizecurDragRows = curDragRows.size();
        qSort(curDragRows.begin(),curDragRows.end(),compareBarData);
        int theRemoveRow = -1;
        for (int i = curDragRows.size()-1;i >= 0;i--) {
            if(curInsertRow < curDragRows.at(i)) theRemoveRow = curDragRows.at(i) + sizecurDragRows;
            else theRemoveRow = curDragRows.at(i);
            model()->removeRow(theRemoveRow);
            curDragRows.removeAt(i);
        }
    }
}
bool compareBarData(const int &barAmount1, const int &barAmount2)
{
    if (barAmount1 < barAmount2)//<升序排列,>降序排列
    {
        return true;
    }
    return false;
}

自定义拖拽分割符样式委托

TableItemDelegate.h

#ifndef TableItemDelegate_H
#define TableItemDelegate_H

#include <QObject>
#include <QPainter>
#include <QStyleOptionViewItem>
#include <QStyledItemDelegate>
#include <QModelIndex>
#include <QTableView>
#include <QDebug>
#include "TableView.h"

#define POLYGON 2   //分隔符等腰三角形直角边长
#define WIDTH 1     //分隔符粗细的一半

class TableItemDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    TableItemDelegate(QObject *parent = nullptr);

protected:
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};

#endif // TableItemDelegate_H

TableItemDelegate.cpp

#include "TableItemDelegate.h"

TableItemDelegate::TableItemDelegate(QObject *parent)
    : QStyledItemDelegate(parent)
{}
void TableItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
    TableView *dragView = qobject_cast<TableView *>(option.styleObject);
    bool isDraging = dragView->isDraging();

    QRect rect = option.rect;

    painter->setRenderHint(QPainter::Antialiasing, true);
    painter->setPen(Qt::NoPen);

    if(option.state & (QStyle::State_MouseOver | QStyle::State_Selected)){
        if(option.state & QStyle::State_Selected){
            painter->setBrush(QColor(180, 0, 0));
            painter->drawRect(rect.topLeft().x(), rect.topLeft().y(), 4, rect.height());

            painter->setBrush(QColor(230, 231, 234));
            painter->drawRect(rect.topLeft().x() + 4, rect.topLeft().y(), rect.width() - 4, rect.height());
        }
    }
//开始绘制拖拽分隔符
    if(isDraging){
        int theDragRow = dragView->dragRow();
        int UpRow = dragView->highlightedRow();
        int DownRow = UpRow + 1;
        int rowCount = dragView->model()->rowCount() - 1;

//绘制DropIndicator
        if(index.row() == UpRow && index.row() != theDragRow - 1 && index.row() != theDragRow){
            painter->setBrush(QColor(0, 0, 0));

            if(UpRow == rowCount){
                //到控件达尾部行,三角形向上移动一个WIDTH的距离,以使分隔符宽度*2
                QPolygon triangle_polygon_bottomLeft;
                triangle_polygon_bottomLeft << QPoint(rect.bottomLeft().x(), rect.bottomLeft().y() - (POLYGON + WIDTH) + 1 - WIDTH);
                triangle_polygon_bottomLeft << QPoint(rect.bottomLeft().x(), rect.bottomLeft().y() - WIDTH + 1 - WIDTH);
                triangle_polygon_bottomLeft << QPoint(rect.bottomLeft().x() + POLYGON, rect.bottomLeft().y() - WIDTH + 1 - WIDTH);

                QPolygon triangle_polygon_bottomRight;
                triangle_polygon_bottomRight << QPoint(rect.bottomRight().x() + 1, rect.bottomRight().y() - (POLYGON + WIDTH) + 1 - WIDTH);
                triangle_polygon_bottomRight << QPoint(rect.bottomRight().x() + 1, rect.bottomRight().y() - WIDTH + 1 - WIDTH);
                triangle_polygon_bottomRight << QPoint(rect.bottomRight().x() - POLYGON + 1, rect.bottomRight().y() - WIDTH + 1 - WIDTH);

                painter->drawRect(rect.bottomLeft().x(), rect.bottomLeft().y() - 2 * WIDTH + 1, rect.width(), 2 * WIDTH);  //rect
                painter->drawPolygon(triangle_polygon_bottomLeft);
                painter->drawPolygon(triangle_polygon_bottomRight);
            }
            else {
                //正常情况,组成三角形上半部分(+1根据实际情况自定义)
                QPolygon triangle_polygon_bottomLeft;
                triangle_polygon_bottomLeft << QPoint(rect.bottomLeft().x(), rect.bottomLeft().y() - (POLYGON + WIDTH) + 1);
                triangle_polygon_bottomLeft << QPoint(rect.bottomLeft().x(), rect.bottomLeft().y() - WIDTH + 1);
                triangle_polygon_bottomLeft << QPoint(rect.bottomLeft().x() + POLYGON, rect.bottomLeft().y() - WIDTH + 1);

                QPolygon triangle_polygon_bottomRight;
                triangle_polygon_bottomRight << QPoint(rect.bottomRight().x() + 1, rect.bottomRight().y() - (POLYGON + WIDTH) + 1);
                triangle_polygon_bottomRight << QPoint(rect.bottomRight().x() + 1, rect.bottomRight().y() - WIDTH + 1);
                triangle_polygon_bottomRight << QPoint(rect.bottomRight().x() - POLYGON + 1, rect.bottomRight().y() - WIDTH + 1);

                painter->drawRect(rect.bottomLeft().x(), rect.bottomLeft().y() - WIDTH + 1, rect.width(), WIDTH);  //rect
                painter->drawPolygon(triangle_polygon_bottomLeft);
                painter->drawPolygon(triangle_polygon_bottomRight);

            }
        }
        else if(index.row() == DownRow && index.row() != theDragRow + 1 && index.row() != theDragRow){
            painter->setBrush(QColor(0, 0, 0));
            if(DownRow == 0){
                //到控件达头部行,三角形向下移动一个WIDTH的距离,以使分隔符宽度*2
                QPolygon triangle_polygon_topLeft;
                triangle_polygon_topLeft << QPoint(rect.topLeft().x(), rect.topLeft().y() + (POLYGON + WIDTH) + WIDTH);
                triangle_polygon_topLeft << QPoint(rect.topLeft().x(), rect.topLeft().y() + WIDTH + WIDTH);
                triangle_polygon_topLeft << QPoint(rect.topLeft().x() + POLYGON, rect.topLeft().y() + WIDTH + WIDTH);

                QPolygon triangle_polygon_topRight;
                triangle_polygon_topRight << QPoint(rect.topRight().x() + 1, rect.topRight().y() + (POLYGON + WIDTH) + WIDTH);
                triangle_polygon_topRight << QPoint(rect.topRight().x() + 1, rect.topRight().y() + WIDTH + WIDTH);
                triangle_polygon_topRight << QPoint(rect.topRight().x() - POLYGON + 1, rect.topRight().y() + WIDTH + WIDTH);

                painter->drawRect(rect.topLeft().x(), rect.topLeft().y(), rect.width(), 2 * WIDTH);  //rect
                painter->drawPolygon(triangle_polygon_topLeft);
                painter->drawPolygon(triangle_polygon_topRight);
            }
            else{
                //正常情况,组成三角形下半部分(+1根据实际情况自定义)
                QPolygon triangle_polygon_topLeft;
                triangle_polygon_topLeft << QPoint(rect.topLeft().x(), rect.topLeft().y() + (POLYGON + WIDTH));
                triangle_polygon_topLeft << QPoint(rect.topLeft().x(), rect.topLeft().y() + WIDTH);
                triangle_polygon_topLeft << QPoint(rect.topLeft().x() + POLYGON, rect.topLeft().y() + WIDTH);

                QPolygon triangle_polygon_topRight;
                triangle_polygon_topRight << QPoint(rect.topRight().x() + 1, rect.topRight().y() + (POLYGON + WIDTH));
                triangle_polygon_topRight << QPoint(rect.topRight().x() + 1, rect.topRight().y() + WIDTH);
                triangle_polygon_topRight << QPoint(rect.topRight().x() - POLYGON + 1, rect.topRight().y() + WIDTH);
                
                painter->drawRect(rect.topLeft().x(), rect.topLeft().y(), rect.width(), WIDTH);  //rect
                painter->drawPolygon(triangle_polygon_topLeft);
                painter->drawPolygon(triangle_polygon_topRight);
            }
        }
        QStyledItemDelegate::paint(painter, option, index);
        return;
    }
//结束绘制拖拽分隔符
    QStyledItemDelegate::paint(painter, option, index);
}

调用

/**! 拖拽行排序,使用方法
 *  ui控件可直接提升此类
 *  代码直接 new TableView(); 即可
 */

TableView* tableView = new TableView();
//分隔符样式委托
TableItemDelegate *delegate = new TableItemDelegate();
tableView->setItemDelegate(delegate);

QStandardItemModel *model = new QStandardItemModel();
model->setColumnCount(3);
model->setHeaderData(0,Qt::Horizontal,QString::fromLocal8Bit("序号"));
model->setHeaderData(1,Qt::Horizontal,QString::fromLocal8Bit("姓名"));
model->setHeaderData(2,Qt::Horizontal,QString::fromLocal8Bit("部门"));
for(int i = 0; i < 5; i++)
{
     model->setItem(i,0,new QStandardItem(QString::number(i+1)));
     model->setItem(i,1,new QStandardItem(QString::fromLocal8Bit("程序%1").arg(QString::number(i+1))));
     model->setItem(i,2,new QStandardItem(QString::fromLocal8Bit("%1部").arg(QString::number(i+1))));
}
QGridLayout *layout = new QGridLayout(this);
layout->setSpacing(0);
layout->addWidget(tableView);
layout->setContentsMargins(0, 0, 0, 0);
this->setLayout(layout);
  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: QTableView是Qt中的一个控件,用于显示表格数据。而自定义Model则是指用户可以自己编写一个继承自QAbstractTableModel的类,来实现自己的数据模型。这样,用户就可以通过自定义Model来控制QTableView中显示的数据,包括数据的格式、排序、过滤等等。自定义Model是Qt中非常常见的一种技术,可以帮助用户更好地管理和展示数据。 ### 回答2: QTableView是Qt中用于展示表格数据的控件,在实际应用中经常需要自定义QTableView的数据模型,以满足业务需求。自定义QTableView的数据模型分为两种,一种是基于QAbstractTableModel的数据模型,另一种是基于QStandardItemModel的数据模型。 使用QAbstractTableModel自定义QTableView的模型,需要继承该类并实现以下虚函数:rowCount()、columnCount()、headerData()和data()。其余函数可根据实际需求决定是否实现,或者使用父类的默认实现。在实现完成后,需要在QTableView中设置该模型,通过setModel()函数进设置。 使用QStandardItemModel自定义QTableView的模型,需要生成一定数量的QStandardItem作为数据单元,使用setItem()函数将数据单元插入到表格中,再通过setHorizontalHeaderLabels()和setVerticalHeaderLabels()设置列头。在实现完成后,需要在QTableView中设置该模型,通过setModel()函数进设置。 自定义QTableView的模型可进以下操作:插入或删除数据、修改数据、添加或删除列、排序等。实现这些操作需要在自定义数据模型中添加相应的函数,同时需要在QTableView中建立信号与槽的连接,以使模型与视图之间保持相互响应。例如,添加一数据可使用insertRow()函数实现,删除一数据可使用removeRow()函数实现,修改单元格数据可使用setData()函数实现,添加一列数据可使用insertColumn()函数实现,删除一列数据可使用removeColumn()函数实现。 总体来说,自定义QTableView的模型可以提高表格数据的可读性和可操作性,满足不同需求的表格数据展示。 ### 回答3: QTableView是Qt中常用的控件之一,它用于展示表格数据。QTableView可以通过使用自定义的model来实现对表格数据的更加灵活的操作。 自定义model是指重写QAbstractTableModel的子类,以实现控制table view中数据的为。重要的是实现rowCount、columnCount、data、headerData函数。函数的重写规则与父类相同,rowCount和columnCount分别返回表格的数和列数,data返回表格中指定位置的数据,headerData则返回表格中每列的标题。 接下来,我们可以利用自定义model来实现对表格的排序、编辑、删除等操作。 排序操作可以通过重写自定义model的sort函数来实现。sort函数接受一个column index和排序类型作为参数,然后在model内部对数据进排序并重新发射数据变更信号通知QTableView更新。 编辑操作可以通过重写自定义model的setData函数来实现。setData接受row index、column index、新的数据作为参数,并在model内部更新数据,并重新发射数据变更信号通知QTableView更新。 删除操作也可以通过重写自定义model的removeRows函数来实现。removeRows接受row index、count作为参数,并在model内部删除相应的数据,并重新发射数据变更信号通知QTableView更新。 总之,自定义model为QTableView提供了一个可扩展的表格数据界面,通过实现自定义model,我们可以更好地控制、管理和操作表格中的数据。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值