QGraphicsItem图元旋转缩放和自定义图元(三)

系列文章目录

QGraphicsItem图元的简单使用(一)
QGraphicsItem图元拖动绘制(二)

前言

接上一章,图元绘制出来了,但有时候需要对图元进行缩放或旋转处理,这章先讲解如何调用图元自带函数来对图元进行缩放、旋转等操作;

一、缩放和旋转

先讲解下如何使用图元自带的缩放和旋转函数,后面自己派生QGraphicsItem时就可以通过鼠标拖动选中图元来实现缩放和旋转了;
简单处理,通过重写场景类的鼠标滑轮事件来实现,具体代码如下:

void GraphicsScene::wheelEvent(QGraphicsSceneWheelEvent *event)
{
    // 获取当前选中的图元
    QList<QGraphicsItem *> listItem = this->selectedItems();
    foreach (QGraphicsItem *item, listItem)
    {
        // 返回转轮旋转的距离,以八分之一度(1/8秒)为单位。正值表示滚轮向前旋转,远离用户;负值表示滚轮向用户方向向后旋转
        int iDelta = event->delta();

        if (item->type() == QGraphicsEllipseItem::Type)     // 椭圆进行缩放处理
        {
            QGraphicsEllipseItem* pEllipse = qgraphicsitem_cast<QGraphicsEllipseItem*>(item);
            if(pEllipse != nullptr)
            {
                // 获取图元绘图区域的矩阵,也就是图元被选中时那个虚线框
                QRectF rect = pEllipse->boundingRect();
                // 因为图元默认的缩放原点为(0,0),放大时图元朝着右下脚偏移,缩小时图元朝着左上角偏移
                // 所以设置图元中心点为缩放原点
                pEllipse->setTransformOriginPoint(rect.center());
                if(iDelta > 0)  // 放大
                {
                    qreal dRate = 1.1;  // 每次放大10%
                    pEllipse->setScale(pEllipse->scale() * dRate);
                }
                else if(iDelta < 0) // 缩小
                {
                    qreal dRate = 0.9;  // 每次缩小10%
                    pEllipse->setScale(pEllipse->scale() * dRate);
                }
            }
        }
        else if(item->type() == QGraphicsLineItem::Type)    // 直线进行旋转处理
        {
            QGraphicsLineItem* pLine = qgraphicsitem_cast<QGraphicsLineItem*>(item);
            if(pLine != nullptr)
            {
                QRectF rect = pLine->boundingRect();
                // 因为图元默认的旋转原点为(0,0),所以设置图元中心点为缩放原点
                pLine->setTransformOriginPoint(rect.center());
                if(iDelta > 0)  // 逆时针
                {
                    // 鼠标滑轮每滑动一次,直线旋转10°
                    pLine->setRotation(pLine->rotation() + 10);
                }
                else if(iDelta < 0) // 顺时针
                {
                    pLine->setRotation(pLine->rotation() - 10);
                }
            }
        }
    }

    return QGraphicsScene::wheelEvent(event);
}

二、自定义图元

通过继承QGraphicsItem图元类,来实现一个矩形图元,演示图如下:
在这里插入图片描述
头文件示例代码如下:

// .h文件
#include <QObject>
#include <QGraphicsItem>

// 根据自己需要是否继承QObject,如果需要信号和槽则必须继承;另外,QObject必须放在前面
class RectItem : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    RectItem(const QRectF& rect = QRectF(), QGraphicsItem *parent = nullptr);

    // 图元边界函数,必须实现
    QRectF boundingRect() const override;

    void setRect(const QRectF& rect){ m_rect = rect; this->update(); }

    // 设置绘制标志
    void SetDrawFlag(bool bDrawFlag){ m_bDrawFlag = bDrawFlag; }

protected:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;

	// 鼠标移动事件,需要特殊处理,防止鼠标拖动绘制时图元移动
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;

private:
	// 需要绘制的矩形
    QRectF m_rect;

    // 绘制标志,防止第一次鼠标拖动绘制时,图元的鼠标移动事件触发,使得图元移动
    bool m_bDrawFlag = false;
};

源文件示例代码如下:

// .cpp文件
#include "RectItem.h"
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsSceneMouseEvent>

RectItem::RectItem(const QRectF &rect, QGraphicsItem *parent): QGraphicsItem(parent), m_rect(rect)
{
    // 设置可移动、可选择、接收焦点等属性
    this->setFlags(QGraphicsItem::ItemIsMovable |
//                   QGraphicsItem::ItemIsFocusable |
                   QGraphicsItem::ItemIsSelectable);
}

QRectF RectItem::boundingRect() const
{
    // 设置图元绘制边界距离图元两个像素
    qreal dAdjust = 5;

    return m_rect.adjusted(-dAdjust, -dAdjust, dAdjust, dAdjust);
}

void RectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    // 重绘函数,绘制矩形
    Q_UNUSED(widget);

    // 设置画笔和画刷
    painter->setPen(QPen(Qt::black, 1));
    painter->setBrush(Qt::green);
    // 绘制矩形
    painter->drawRect(m_rect);

    // 绘制选中时的虚框
    if (option->state & QStyle::State_Selected)
    {
        // 获取图元绘制区域
        QRectF rect = this->boundingRect();

        // 绘制虚线框
        painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine));
        painter->setBrush(Qt::NoBrush);

        // 设置虚线框距离绘图区域的间距,因为画笔有宽度
        const qreal pad = painter->pen().widthF() / 2;

        painter->drawRect(rect.adjusted(pad, pad, -pad, -pad));
    }
}

void RectItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    // 左键按下,并且当前处于绘制状态
    if (event->buttons() == Qt::LeftButton && m_bDrawFlag)
    {
        return;
    }
    return QGraphicsItem::mouseMoveEvent(event);
}

直接添加到之前创建的工程,替换QGraphicsRectItem图元类即可,另外需要在场景的鼠标按下时,调用图元的设置绘制标志SetDrawFlag函数设置标志为true,鼠标释放时设置标志为false;

关于为什么QGraphicsRectItem在鼠标拖动绘制时,图元不会跟着移动,我看了下Qt源码,没找到具体原因,知道的大佬可以评论区留言


总结

下一节,将介绍如何使用鼠标拖动缩放图元

  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
要支持键盘输入焦点和按键事件,QGraphicsItem需要继承自QGraphicsItem类,并且重写下列方法: 1. void keyPressEvent(QKeyEvent *event):重写keyPressEvent方法处理按键事件; 2. QVariant itemChange(GraphicsItemChange change, const QVariant &value):重写itemChange方法处理焦点事件。 在重写keyPressEvent方法时,需要注意: 1. QGraphicsItem没有默认的键盘焦点,因此需要使用setFlag()方法将QGraphicsItemItemIsFocusable标志设置为true,才能接收键盘事件; 2. 在keyPressEvent方法中,需要通过QKeyEvent的key()方法获取按下的键,并根据需要进行处理。 在重写itemChange方法时,需要注意: 1. 当焦点状态改变时,会触发GraphicsItemChange类型的change参数为QGraphicsItem::ItemFocusHasChanged的信号,可以在itemChange方法中重写处理该信号; 2. 在itemChange方法中,可以通过value参数获取焦点状态的改变,并根据需要进行处理。 举例来说,以下代码是QGraphicsItem的一个子类,在该类中实现了键盘输入焦点和按键事件的支持: ```c++ class MyItem : public QGraphicsItem { public: MyItem(QGraphicsItem *parent = nullptr) : QGraphicsItem(parent) { setFlag(ItemIsFocusable, true); } QRectF boundingRect() const override { return QRectF(0, 0, 100, 100); } void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override { painter->drawRect(boundingRect()); } void keyPressEvent(QKeyEvent *event) override { switch (event->key()) { case Qt::Key_Up: setPos(pos() + QPointF(0, -10)); break; case Qt::Key_Down: setPos(pos() + QPointF(0, 10)); break; case Qt::Key_Left: setPos(pos() + QPointF(-10, 0)); break; case Qt::Key_Right: setPos(pos() + QPointF(10, 0)); break; default: break; } } QVariant itemChange(GraphicsItemChange change, const QVariant &value) override { if (change == QGraphicsItem::ItemFocusHasChanged) { if (value.toBool()) { qDebug() << "Item has gained focus!"; } else { qDebug() << "Item has lost focus!"; } } return QGraphicsItem::itemChange(change, value); } }; ``` 在上述代码中,MyItem类重写了QGraphicsItem的keyPressEvent和itemChange方法,实现了键盘输入焦点和按键事件的支持。具体地,该类在构造函数中将ItemIsFocusable标志设置为true,使其能够接受键盘焦点;在keyPressEvent方法中,根据按下的键进行移动;在itemChange方法中,处理焦点状态的改变。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li_Zhi_Yao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值