Qt C++之QGraphicsItem的缩放(包含源代码)

7 篇文章 3 订阅

在QGraphicsScene中,拖动鼠标缩放矩形QGraphicsRectItem的技术探索
需求
1.QGraphicsRectItem m_BlockItem是需要缩放的矩形;
2.鼠标左击按住矩形四个角后拖动,矩形m_BlockItem做相应的放大和缩小;
3.左边的小三角形还需要保持在左边中点附近;
思路
1.先新建一个QGraphicsRectItem m_BlockItem,作为大矩形(目标矩形);
2.在创建四个小正方形QGraphicsRectItem m_SmallRect_0、m_SmallRect_1、m_SmallRect_2、m_SmallRect_3,分别对应m_BlockItem的左上角、右上角、右下角和左下角,然后使用:setParentItem(m_BlockItem)函数,将四个小矩形放入m_BlockItem中。
如有不懂欢迎回复交流:邮箱:xingyu.cat@foxmail.com
运行效果图:

缩小
放大

上代码

SimuLinkScene.h

#ifndef SIMULINKSCENE_H
#define SIMULINKSCENE_H

#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QGraphicsSimpleTextItem>
#include <QGraphicsPolygonItem>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
class SimuLinkScene : public QGraphicsScene
{
    Q_OBJECT
public:
    explicit SimuLinkScene(QObject *parent = Q_NULLPTR);

    int cursorType = 0;
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
    void keyPressEvent(QKeyEvent *event) override;
private:
	//目标大矩形的
    QGraphicsRectItem *m_BlockItem;
    //矩形的名称
    QGraphicsSimpleTextItem *m_BlockNameTextItem;
    //矩形左边的小三角形
    QGraphicsPolygonItem *m_PortItem;
    QGraphicsRectItem *m_SmallRect_0;
    QGraphicsRectItem *m_SmallRect_1;
    QGraphicsRectItem *m_SmallRect_2;
    QGraphicsRectItem *m_SmallRect_3;
    
	// m_BlockItem的左上角在场景中的坐标
    QPointF point1;
    // m_BlockItem的右下角在场景中的坐标
    QPointF point2;
	//标记鼠标是否被按下
    bool m_LeftButton_IsPressed = false;
};

#endif // SIMULINKSCENE_H

SimuLinkScene.cpp

#include "SimuLinkScene.h"

SimuLinkScene::SimuLinkScene(QObject *parent )
{
    Q_UNUSED(parent);
    this->setSceneRect(0, 0, 6000, 1200);

    m_BlockItem = new QGraphicsRectItem;
    m_BlockNameTextItem = new QGraphicsSimpleTextItem;
    m_PortItem = new QGraphicsPolygonItem;

    m_SmallRect_0 = new QGraphicsRectItem;
    m_SmallRect_1 = new QGraphicsRectItem;
    m_SmallRect_2 = new QGraphicsRectItem;
    m_SmallRect_3 = new QGraphicsRectItem;

    // 用于判断是哪个类型的Item
    m_BlockItem->setData(1,1);
    m_BlockNameTextItem->setData(1,2);
    m_PortItem->setData(1,3);
    m_SmallRect_0->setData(1,4);
    m_SmallRect_1->setData(1,4);
    m_SmallRect_2->setData(1,4);
    m_SmallRect_3->setData(1,4);

    // 这是矩形框线的粗细
    int item_width = 1;
    QPen pen(Qt::black, item_width, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);

    // 前两个参数:表示放入父项目的坐标(如果没有父项目就是对应场景中坐标)
    // 后两个参数:表示宽和高
    m_BlockItem->setRect(0,0,150,80);
    m_BlockItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);


    // 将m_BlockNameTextItem放入到m_BlockItem中,此时m_BlockNameTextItem就有了父类调用setPos的坐标都是相对于m_BlockItem的左上角来的
    m_BlockNameTextItem->setParentItem(m_BlockItem);
    m_BlockNameTextItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
    QFont font = m_BlockNameTextItem->font();
    //  像素大小
    font.setPixelSize(10);
    // font.setItalic(true);  //  斜体
    // font.setUnderline(true);  //  下划线
    m_BlockNameTextItem->setFont(font);
    m_BlockNameTextItem->setText("ttt");
    m_BlockNameTextItem->setPos(37.5,40);

    // 将m_PortItem放入到m_BlockItem中,此时m_PortItem就有了父类调用setPos的坐标都是相对于m_BlockItem的左上角来的
    m_PortItem->setParentItem(m_BlockItem);
    // 因为多边形需要通过坐标数组
    QVector<QPointF> v_point;
    v_point.append(QPointF(0,40));
    v_point.append(QPointF(-10,30));
    v_point.append(QPointF(-10,50));
    m_PortItem->setPolygon(v_point);
    m_PortItem->setZValue(1);
    m_PortItem->setPen(QPen(Qt::black, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));

    // 将m_SmallRect_0放入到m_BlockItem中,此时m_SmallRect_0就有了父类调用setPos的坐标都是相对于m_BlockItem的左上角来的
    m_SmallRect_0->setParentItem(m_BlockItem);
    m_SmallRect_1->setParentItem(m_BlockItem);
    m_SmallRect_2->setParentItem(m_BlockItem);
    m_SmallRect_3->setParentItem(m_BlockItem);

    //设置项目Item可以被选择和可以成为焦点项
    m_SmallRect_0->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
    m_SmallRect_1->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
    m_SmallRect_2->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
    m_SmallRect_3->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);

    // 设置m_SmallRect_0的图层级别,数值越大越在最上面,默认是0
    m_SmallRect_0->setZValue(2);
    m_SmallRect_1->setZValue(2);
    m_SmallRect_2->setZValue(2);
    m_SmallRect_3->setZValue(2);


    // 标记m_SmallRect_0在哪个角(0对应左上角、1对应右上角、2对应右下角、3对应左下角)
    m_SmallRect_0->setData(3,0);
    m_SmallRect_1->setData(3,1);
    m_SmallRect_2->setData(3,2);
    m_SmallRect_3->setData(3,3);

    // 设置m_SmallRect_0的边框线
    m_SmallRect_0->setPen(pen);
    m_SmallRect_1->setPen(pen);
    m_SmallRect_2->setPen(pen);
    m_SmallRect_3->setPen(pen);

    // m_SmallRect_0的左上角画在以m_BlockItem的左上角为原点的(-4,-4)处。宽为8,高为8
    m_SmallRect_0->setRect(-4,-4,8,8);
    m_SmallRect_1->setRect(146,-4,8,8);
    m_SmallRect_2->setRect(146,76,8,8);
    m_SmallRect_3->setRect(-4,76,8,8);

    this->addItem(m_BlockItem);

}

void SimuLinkScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mousePressEvent(event);
    QGraphicsItem *item = focusItem();

    qDebug()<<"获取对象"<<item->data(1).toInt();
    qDebug()<<"width:"<<m_BlockItem->boundingRect().width()<<";height:"<<m_BlockItem->boundingRect().height();
    qDebug()<<"TextItemPos:"<<m_BlockNameTextItem->mapToScene(m_BlockNameTextItem->pos().x(),m_BlockNameTextItem->pos().y())-m_BlockNameTextItem->pos()<<";Pos:"<<m_BlockNameTextItem->pos();
    qDebug()<<"m_BlockItem_pos:"<<m_BlockItem->scenePos();
    qDebug()<<"Cursor:"<<event->scenePos();

    // 要用setX和setY不要使用 point1 = QPointF(x,y);
    // m_BlockItem->scenePos():是m_BlockItem左上角的点在场景Scene中的坐标
    point1.setX(m_BlockItem->scenePos().x());
    point1.setY(m_BlockItem->scenePos().y());

    point2.setX(m_BlockItem->scenePos().x()+m_BlockItem->boundingRect().width());
    point2.setY(m_BlockItem->scenePos().y()+m_BlockItem->boundingRect().height());

    qDebug()<<"point1="<<point1;
    qDebug()<<"point2="<<point2;
    if(event->button() == Qt::LeftButton)
    {
        m_LeftButton_IsPressed = true;
    }
}

void SimuLinkScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mouseReleaseEvent(event);
    if(event->button() == Qt::LeftButton)
    {
        m_LeftButton_IsPressed = false;
    }
}

void SimuLinkScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mouseDoubleClickEvent(event);
}

void SimuLinkScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{

    QGraphicsScene::mouseMoveEvent(event);

    // 获取场景中的焦点Item,需要Item设置该属性 item->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
    QGraphicsItem *currentItem = focusItem();

    // 至于这里的点为什么使用qreal(double)型,参考该博主:https://blog.csdn.net/m0_37545861/article/details/117930243?spm=1001.2014.3001.5501
    qreal scene_X = event->scenePos().x();
    qreal scene_Y = event->scenePos().y();

    // 如果小正方形被鼠标点击了成为了焦点item,
    if( currentItem!=nullptr && currentItem->data(1).toInt() == 4 && m_LeftButton_IsPressed){

        int type = currentItem->data(3).toInt();
        switch (type) {
        // 如果点击的是左上角的小正方形
        case 0:
            cursorType = 0;
            // 矩形的左上角坐标是光标的坐标
            point1.setX(scene_X);   //注意这里的是场景中光标的坐标
            point1.setY(scene_Y);
            // 矩形左下角不变
            point2.setX(point2.x());
            point2.setY(point2.y());

            m_BlockItem->setRect(0,0,point2.x()-point1.x(),point2.y()-point1.y());
            m_BlockItem->setPos(point1);

            m_SmallRect_0->setRect(-4,-4,8,8);
            m_SmallRect_1->setRect(m_BlockItem->boundingRect().width()-4,-4,8,8);
            m_SmallRect_2->setRect(m_BlockItem->boundingRect().width()-4,m_BlockItem->boundingRect().height()-4,8,8);
            m_SmallRect_3->setRect(-4,m_BlockItem->boundingRect().height()-4,8,8);

            break;
        // 如果点击的是右上角的小正方形
        case 1:
            cursorType = 1;
            // 左上角的x坐标还是原来的x坐标
            point1.setX(point1.x());
            // 左上角的Y坐标需要跟随光标的Y坐标
            point1.setY(scene_Y);

            point2.setX(scene_X);
            point2.setY(point2.y());

            m_BlockItem->setRect(0,0,point2.x()-point1.x(),point2.y()-point1.y());
            m_BlockItem->setPos(point1);

            m_SmallRect_0->setRect(-4,-4,8,8);
            m_SmallRect_1->setRect(m_BlockItem->boundingRect().width()-4,-4,8,8);
            m_SmallRect_2->setRect(m_BlockItem->boundingRect().width()-4,m_BlockItem->boundingRect().height()-4,8,8);
            m_SmallRect_3->setRect(-4,m_BlockItem->boundingRect().height()-4,8,8);
            break;
        // 如果点击的是右下角的小正方形
        case 2:
            cursorType = 0;

            point1.setX(point1.x());
            point1.setY(point1.y());

            point2.setX(scene_X);
            point2.setY(scene_Y);

            m_BlockItem->setRect(0,0,point2.x()-point1.x(),point2.y()-point1.y());
            m_BlockItem->setPos(point1);

            m_SmallRect_0->setRect(-4,-4,8,8);
            m_SmallRect_1->setRect(m_BlockItem->boundingRect().width()-4,-4,8,8);
            m_SmallRect_2->setRect(m_BlockItem->boundingRect().width()-4,m_BlockItem->boundingRect().height()-4,8,8);
            m_SmallRect_3->setRect(-4,m_BlockItem->boundingRect().height()-4,8,8);
            break;
        case 3:
            cursorType = 1;

            point1.setX(scene_X);
            point1.setY(point1.y());

            point2.setX(point2.x());
            point2.setY(scene_Y);

            m_BlockItem->setRect(0,0,point2.x()-point1.x(),point2.y()-point1.y());
            m_BlockItem->setPos(point1);


            m_SmallRect_0->setRect(-4,-4,8,8);
            m_SmallRect_1->setRect(m_BlockItem->boundingRect().width()-4,-4,8,8);
            m_SmallRect_2->setRect(m_BlockItem->boundingRect().width()-4,m_BlockItem->boundingRect().height()-4,8,8);
            m_SmallRect_3->setRect(-4,m_BlockItem->boundingRect().height()-4,8,8);

            break;
        default:
            break;
        }
        QVector<QPointF> v_point;
        v_point.append(QPointF(0,m_BlockItem->boundingRect().height()/2));
        v_point.append(QPointF(-10,m_BlockItem->boundingRect().height()/2-10));
        v_point.append(QPointF(-10,m_BlockItem->boundingRect().height()/2+10));

        m_PortItem->setPolygon(v_point);

        QPointF p1 = QPointF(m_BlockItem->boundingRect().width()/2-m_BlockNameTextItem->boundingRect().width()/2,m_BlockItem->boundingRect().height());
        m_BlockNameTextItem->setPos(p1);
    }
}

void SimuLinkScene::keyPressEvent(QKeyEvent *event)
{
    QGraphicsScene::keyPressEvent(event);
}


  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
可以通过重载 QGraphicsItem 的 `mouseMoveEvent` 函数来实现只沿着 X 轴拖动图形的功能。 下面是一个简单的实现示例: ```cpp class MyItem : public QGraphicsItem { public: MyItem(QGraphicsItem *parent = nullptr) : QGraphicsItem(parent) { // 设置图形的初始位置和大小 setPos(0, 0); m_width = 100; m_height = 100; } QRectF boundingRect() const override { return QRectF(0, 0, m_width, m_height); } void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override { // 绘制图形 painter->drawRect(boundingRect()); } void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override { // 获取当前鼠标位置和上一次鼠标位置之间的差值 QPointF delta = event->pos() - event->lastPos(); // 只允许沿着 X 轴方向移动图形 QPointF newPos = pos() + QPointF(delta.x(), 0); // 限制图形的移动范围 if (newPos.x() < 0) newPos.setX(0); if (newPos.x() + m_width > scene()->width()) newPos.setX(scene()->width() - m_width); // 更新图形的位置 setPos(newPos); } private: qreal m_width; qreal m_height; }; ``` 在上面的示例中,我们重载了 `mouseMoveEvent` 函数,并在其中实现了只沿着 X 轴方向拖动图形的逻辑。具体来说,当鼠标拖动图形时,我们计算出当前鼠标位置和上一次鼠标位置之间的差值,并将其加到图形的当前位置上,从而实现了图形的移动。同时,我们还限制了图形的移动范围,使其不能超出场景的边界。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

橘猫掸子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值