Qt绘图与图形视图之自定义图元实现拖拽、拉伸、旋转功能

往期回顾

Qt绘图与图形视图之移动鼠标手动绘制任意多边形的简单介绍-CSDN博客

Qt绘图与图形视图之场景、视图架构的简单介绍-CSDN博客

Qt绘图与图形视图之基本图元绘制的简单介绍-CSDN博客

 Qt绘图与图形视图之自定义图元实现拖拽、拉伸、旋转功能

一、最终效果

实现对自定义图元的旋转,拖拽、拉伸功能

二、主要思路

主要是三个需要处理的点,1、自己创建一个自定义图元,2、图元的旋转,拖拽、拉伸功能逻辑实现,3、鼠标样式改变 

1、主类的初始化

最开始在主类里进行的初始化

这个很简单,没什么好说的,记得顺序,是先视图,再是创建场景,创建图元并添加

ch88_CustomRectItem::ch88_CustomRectItem(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);

    QRect rect = ui.graphicsView->rect();

    // 创建场景
    QGraphicsScene* pScene = new QGraphicsScene(rect);

    // 视图里设置场景
    ui.graphicsView->setScene(pScene);

    // 创建自定义图元
    MyRectItem* pRectItem = new MyRectItem();

    // 场景添加图元
    pScene->addItem(pRectItem);
}

三、如何自定义图元

 1、设计思路

首先创建一个类继承自QGraphicsItem的,重写其父类的虚函数,重绘图案,本身调用函数可以重绘的图案有很多种的,重写鼠标事件,最后处理旋转的逻辑实现

2、新建C++类派生于QGraphicsItem

注意新建一个C++类,是需要直接派生于QGraphicsItem

class MyRectItem : public QObject, public QGraphicsItem
{
    Q_OBJECT

public:
    MyRectItem(QGraphicsItem* parent = nullptr);
    ~MyRectItem();

    // 必须重写该函数
    QRectF  boundingRect() const override;

这里为什么必须重写该函数? 

因为它本身是QGraphicsItem类的一个虚函数,如果不重写,那么MyRectItem类将也是一个虚函数类,无法创建对象的

virtual QRectF boundingRect() const = 0;

这个函数的作用是返回该图形项的边界矩形,即包围该图形项的最小矩形区域

QRectF MyRectItem::boundingRect() const
{
    QRectF boundingRectF = m_oldRectPolygon.boundingRect();
    return QRectF(boundingRectF.x() - 40, boundingRectF.y() - 40, 
        boundingRectF.width() + 80, boundingRectF.height() + 80);
}

这个boundingRect()函数会返回一个比实际边界矩形更大一些的矩形区域,这样可以确保在绘制和碰撞检测时有足够的空间。

3、构造函数初始化

构造函数里先初始化各项值,设置矩形,此外还要设置矩形的大小和位置

MyRectItem::MyRectItem(QGraphicsItem* parent) :
    m_bResize(false),
    m_oldRect(0, 0, 200, 200),
    m_bRotate(false),
    m_RotateAngle(0),
    m_StateFlag(DEFAULT_FLAG)
{
    setRectSize(m_oldRect);
    
    //设置光标形状,手的形状
    setCursor(Qt::ArrowCursor);

    //设置图元是可移动的
    setFlags(QGraphicsItem::ItemIsMovable
        | QGraphicsItem::ItemIsSelectable
        | QGraphicsItem::ItemIsFocusable);

    m_pPointFofSmallRotateRect = new QPointF[4];
    SetRotate(0);
}

4、重写鼠标事件函数 

另外还需要重写一下这四个鼠标事件函数

注意参数类型,不再是QMouseEvent了,而是QGraphicsSceneMouseEvent 

    void paint(QPainter* painter,
        const QStyleOptionGraphicsItem* option,
        QWidget* widget) override;

    void mousePressEvent(QGraphicsSceneMouseEvent* event) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override;

后面的几个思路:

4.1、绘制旋转后的矩形和圆形
4.2、 鼠标样式改变

主要是在鼠标按下事件里处理,毕竟确实是要鼠标按下了,样式才会改变,逻辑判断还是比较简单的,if-else语句先判断鼠标的位置,然后根据位置设置样式即可

4.3、鼠标移动的状态判断

鼠标移动这里的判断比较复杂,主要是根据m_StateFlag的状态来判断当前是旋转、移动矩形还是移动矩形的边线,然后再根据不同的状态进行不同的处理

1、如果是旋转矩形状态,根据鼠标位置计算旋转角度,并调用SetRotate函数设置旋转角度,然后调用setRectSize函数恢复矩形的位置和大小,并更新场景

2、如果是移动矩形状态(MOV_RECT),则根据鼠标移动的距离调用moveBy函数移动矩形,然后调用setRectSize函数恢复矩形的位置和大小,并更新场景 

3、如果是移动矩形的边线状态(MOV_LEFT_LINE、MOV_TOP_LINE、MOV_RIGHT_LINE、MOV_BOTTOM_LINE)则根据鼠标位置计算新的矩形边界,调用setRectSize函数设置新的矩形大小,并更新场景。 

4.4、鼠标松开后状态判断

主要就是判断是不是在旋转状态,如果是那么需要恢复状态为默认值,只要不是旋转,就不改变状态,所以迭代。

5、最难的旋转功能实现

旋转逻辑实现,拉伸拖拽功能都还好,比较难的是旋转功能的实现,毕竟旋转角度很多,而且旋转后每个点的位置坐标都将改变,需要一一获取

5.1、设置旋转角度
void MyRectItem::SetRotate(qreal RotateAngle, QPointF ptCenter)
{
    m_bRotate = true;
    if (ptCenter.x() == -999 && ptCenter.y() == -999)
    {
        m_RotateCenter = QPointF(m_oldRect.x() + m_oldRect.width() / 2, m_oldRect.y() + m_oldRect.height() / 2);
    }
    else
    {
        m_RotateCenter = ptCenter;
    }

    m_RotateAngle = RotateAngle;
    this->update();
}
5.2、获取旋转后的点
QPointF MyRectItem::getRotatePoint(QPointF ptCenter, QPointF ptIn, qreal angle)
{
    double dx = ptCenter.x();
    double dy = ptCenter.y();
    double x = ptIn.x();
    double y = ptIn.y();
    double xx, yy;
    xx = (x - dx) * cos(angle * M_PI / 180) - (y - dy) * sin(angle * M_PI / 180) + dx;
    yy = (x - dx) * sin(angle * M_PI / 180) + (y - dy) * cos(angle * M_PI / 180) + dy;

    return QPointF(xx, yy);
}
5.3、获取旋转后的多个点
QList<QPointF> MyRectItem::getRotatePoints(QPointF ptCenter, QList<QPointF> ptIns, qreal angle)
{
    QList<QPointF> lstPt;
    for (int i = 0; i < ptIns.count(); i++)
    {
        lstPt.append(getRotatePoint(ptCenter, ptIns.at(i), angle));
    }
    return lstPt;
}
5.4、矩形旋转后返回多边形
QPolygonF MyRectItem::getRotatePolygonFromRect(QPointF ptCenter, QRectF rectIn, qreal angle)
{
    QVector<QPointF> vpt;
    QPointF pf = getRotatePoint(ptCenter, rectIn.topLeft(), angle);
    vpt.append(pf);
    pf = getRotatePoint(ptCenter, rectIn.topRight(), angle);
    vpt.append(pf);
    pf = getRotatePoint(ptCenter, rectIn.bottomRight(), angle);
    vpt.append(pf);
    pf = getRotatePoint(ptCenter, rectIn.bottomLeft(), angle);
    vpt.append(pf);
    pf = getRotatePoint(ptCenter, rectIn.topLeft(), angle);
    vpt.append(pf);
    return QPolygonF(vpt);
}
5.5、实时获取旋转时矩形正上方的标记

以上就是Qt里自定义图元实现拖拽、拉伸、旋转功能的简单介绍。

都看到这里了,点个赞再走呗朋友~

加油吧,预祝大家变得更强!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值