Qt自定义控件1:可拖拽排序表格(类似QQ好友分组排序)

来源:互联网 发布:桌面世界时钟软件 编辑:程序博客网 时间:2024/05/16 14:03

1,简介


为了最佳体验,一个拖拽行排序的功能研究了几个小时。效果参考的QQ好友分组的排序。

网上查了下好像没有人发布QT版类似的代码,于是自己动手


QQ好友分组排序效果:



2,效果


这是最终效果图,有小伙伴说看起来很流畅,事实确实是很流畅的。



3,思路


拖拽:就是QT的一套拖拽功能的类,QDrag、QMimeData,和几个鼠标事件和拖拽响应事件:

protected:    //根据鼠标事件开启拖拽    void mousePressEvent ( QMouseEvent * event ) ;    void mouseMoveEvent(QMouseEvent *event);    //拖拽响应事件    void dragEnterEvent(QDragEnterEvent *event);    void dragMoveEvent(QDragMoveEvent *event);    void dropEvent(QDropEvent *event);

拖拽时的位置指示线:其实就是个高度为2的QLabel,设边框颜色就是该线的颜色。


主要类:一个继承自QTableView的类 MyTableView

MyTableView.h:

#ifndef MYTABLEVIEW_H#define MYTABLEVIEW_H#include <QTableView>#include <QLabel>#include <QMouseEvent>#include <QStandardItemModel>class MyTableView : public QTableView{    Q_OBJECTpublic:    MyTableView(QWidget *parent = Q_NULLPTR);    //此函数包装了自带的setModel,只是记录了model指针方便内部调用    void SetModel(QStandardItemModel *model);signals:    //拖拽结束后会发出此信号,可绑定槽函数实现你的其他功能,比如把新的顺序存到文件    void sigRowChange(int from, int to);protected:    //根据鼠标事件开启拖拽    void mousePressEvent ( QMouseEvent * event ) ;    void mouseMoveEvent(QMouseEvent *event);    //拖拽响应事件    void dragEnterEvent(QDragEnterEvent *event);    void dragMoveEvent(QDragMoveEvent *event);    void dropEvent(QDropEvent *event);private:    void DoDrag();                      //执行拖拽    void ResetOrder();                  //重置第一列序号    void MoveRow(int from, int to);     //真正执行移动行的功能private:    QStandardItemModel *mModel;    QLabel* mLabel;             //高度设为2,用做指示线    int mRowHeight;             //表格的行高    bool mValidPress;           //在鼠标移动时,判断之前按下时是否是在有效行上int mRowFrom;//拖动起始行    int mRowTo;                 //拖动时(还没松开时)鼠标位置代表的行,会绘制一条指示线,类似QQ好友列表拖拽效果    QString mDragText;          //拖拽过程中跟随鼠标显示的内容    QPoint mDragPoint;          //拖拽起点    QPoint mDragPointAtItem;    //记录按下时鼠标相对该行的位置,在拖动过程中保持该相对位置};#endif // MYTABLEVIEW_H

MyTableView.cpp:

#include "MyTableView.h"#include <QApplication>#include <QDrag>#include <QMimeData>#include <QPainter>#include <QHeaderView>MyTableView::MyTableView(QWidget *parent)    : QTableView(parent){    setAcceptDrops(true);mLabel = new QLabel(this);mLabel->setFixedHeight(2);    mLabel->setGeometry(1, 0, width(), 2);mLabel->setStyleSheet("border: 1px solid #8B7500;");mLabel->hide();    mModel = NULL;    mRowHeight = 30;    mValidPress = false;    mRowFrom = 0;    mRowTo = 0;}void MyTableView::SetModel(QStandardItemModel *model){    mModel = model;    QTableView::setModel(model);}void MyTableView::mousePressEvent(QMouseEvent *e){    if (e->button() == Qt::LeftButton)    {        QModelIndex index = indexAt(e->pos());        if (index.isValid())        {            mValidPress = true;            mDragPoint = e->pos();            mDragText = mModel->item(index.row(),1)->text();            mDragPointAtItem = mDragPoint - QPoint(0,index.row()*mRowHeight);            mRowFrom = index.row();        }    }    QTableView::mousePressEvent(e);}void MyTableView::mouseMoveEvent ( QMouseEvent * e ){    if (!mValidPress){return;}    if (!(e->buttons() & Qt::LeftButton))        return;    if ((e->pos() - mDragPoint).manhattanLength()  < QApplication::startDragDistance())        return;    mLabel->show();    DoDrag();           //开始拖拽,完成拖拽后才会继续往下走    mLabel->hide();    mValidPress = false;}void  MyTableView::dragEnterEvent ( QDragEnterEvent * e ){    if (e->mimeData()->hasText()){        e->acceptProposedAction();}else{        e->ignore();        QTableView::dragEnterEvent(e);}}void  MyTableView::dragMoveEvent ( QDragMoveEvent * e ){    if(e->mimeData()->hasText())    {int nCurRow = 0;        QModelIndex index = indexAt(e->pos());        if (index.isValid())        {            if(e->pos().y() - index.row()*mRowHeight >= mRowHeight/2)            {                nCurRow = index.row() + 1;            }            else            {                nCurRow = index.row();            }        }        else        {nCurRow = mModel->rowCount();        }//        if (nCurRow != mRowFrom){            mRowTo = nCurRow;mLabel->setGeometry(1, mRowTo*mRowHeight, width() - 2, 2);}        e->acceptProposedAction();return;    }    e->ignore();    QTableView::dragMoveEvent(e);}void  MyTableView::dropEvent ( QDropEvent * e ){    if(e->mimeData()->hasText())    {        if (mRowTo != mRowFrom)        {            MoveRow(mRowFrom, mRowTo);}        e->acceptProposedAction();return;    }    e->ignore();    QTableView::dropEvent(e);}void MyTableView::DoDrag(){    QDrag *drag = new QDrag(this);    QMimeData *mimeData = new QMimeData;    mimeData->setText(mDragText);    drag->setMimeData(mimeData);    // 设置拖拽图片    QPixmap drag_img(width(), mRowHeight);    drag_img.fill(QColor(255,255,255,100));    QPainter painter(&drag_img);    painter.setPen(QColor(0, 0, 0, 200));    painter.drawText(QRectF(40,0,width(),mRowHeight), mDragText, QTextOption(Qt::AlignVCenter));    drag->setPixmap(drag_img);    drag->setHotSpot(mDragPointAtItem);    //***注意:此句会阻塞,进入drag的拖拽消息循环,会依次触发dragEnterEvent、dragMoveEvent、dropEvent消息    if (drag->exec(Qt::MoveAction) == Qt::MoveAction)    {        // 确认移动操作    }}void MyTableView::ResetOrder(){for (int i = 0; i < mModel->rowCount(); i++){        mModel->item(i, 0)->setText( QString::number(i + 1) );}}void MyTableView::MoveRow(int from, int to){if (from == to){return;}QStandardItem * item = mModel->item(from, 1);if (item){        QString strText = item->text();QList<QStandardItem*> items;QStandardItem* item0 = new QStandardItem("");        QStandardItem* item1 = new QStandardItem(strText);items.append(item0);items.append(item1);        item0->setTextAlignment(Qt::AlignCenter);        //移动行思路:就是先在要移动到的位置新建同样内容的行,然后删除被移动的行mModel->insertRow(to, items);if (from < to){mModel->removeRow(from);selectRow(to-1);}else{mModel->removeRow(from+1);selectRow(to);}ResetOrder();        emit sigRowChange(mRowFrom,mRowTo);}}

在MainWindow上放一个QTableView然后提升为该MyTableView,MainWindow里做些初始化:

MainWindow.cpp:

#include "MyTableView.h"#include "mainwindow.h"#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :    QMainWindow(parent),    ui(new Ui::MainWindow){    ui->setupUi(this);    InitTable();}MainWindow::~MainWindow(){    delete ui;}void MainWindow::InitTable(){    MyTableView* t = ui->tableView;    //设置各种属性    t->horizontalHeader()->hide();//隐藏列头(带列头的情况需要修改代码,重新计算高度偏移)    t->verticalHeader()->hide();//隐藏行头    t->verticalHeader()->setDefaultSectionSize(30);         //默认列高度    t->horizontalHeader()->setStretchLastSection(true);     //最后一列自适应宽度    t->setEditTriggers(QTableView::NoEditTriggers);//不能编辑    t->setSelectionBehavior(QTableView::SelectRows);//一次选中整行    t->setSelectionMode(QTableView::SingleSelection);       //单行选中    t->setAlternatingRowColors(true);                       //行间隔色    t->setShowGrid(false);                                  //去掉网格线    t->setFocusPolicy(Qt::NoFocus);                         //去掉item选中时虚线框    //添加数据    QStandardItemModel* model = new QStandardItemModel();    model->setHorizontalHeaderItem(0, new QStandardItem(QStringLiteral("序号")));    model->setHorizontalHeaderItem(1, new QStandardItem(QStringLiteral("内容")));    QStringList strs;    strs<<"111"<<"222"<<"333"<<"444"<<"AAA"<<"BBB"<<"CCC"<<"DDD";    for(int i=0;i<strs.size();i++)    {        model->setItem(i, 0, new QStandardItem(QString::number(i+1)));        model->setItem(i, 1, new QStandardItem(strs[i]));        model->item(i, 0)->setTextAlignment(Qt::AlignCenter);    }    t->SetModel(model);    //设置样式    t->setStyleSheet("QTableView {border: 1px solid gray;background: #E8E8E8;}\                      QTableView::item{color:black;}\                      QTableView::item:selected{color:black;background: #63B8FF;}");    t->setColumnWidth(0,40);    t->selectRow(0);}



4,源码下载


百度网盘:https://pan.baidu.com/s/1dFeqbYT

注:Demo使用QT5.8+MinGW编译通过。




阅读全文
1 0
原创粉丝点击