自定义QGraphcsItem旋转移动缩放组合

简单的记录下学习自定义QGraphicsItem 移动、旋转、缩放、组合。

1. QGraphicsView缩放

通过鼠标滚轮缩放整个视图
重写GraphicsView的wheelEvent(QWheelEvent *event)事件

在这里插入图片描述

//
//QttGraphicsView::QttGraphicsView(QGraphicsScene* scene, QWidget* parent) : QGraphicsView(scene, parent){}
void QttGraphicsView::wheelEvent(QWheelEvent *event)
{
    static float scale = 1.1;

    auto angle = event->angleDelta();

    if(angle.y() > 0)
    {
        this->scale(scale, scale);
    }
    else
    {
        this->scale(1/scale, 1/scale);
    }
}

2.拖动、旋转、缩放

继承重写QGraphicsItem

#ifndef QTTGRAPHICSITEMA_H
#define QTTGRAPHICSITEMA_H

#include <QObject>
#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPixmap>
#include <QRectF>
#include <QVector3D>

//enum MyCursorTypes{
//    Normal      = 0,
//    Move        = 1,
//    Rotate      = 2,
//    LeftTop     = 3,
//    LeftBottom  = 4,
//    RightTop    = 5,
//    RightBottom = 6,
//};

class QttGraphicsItemCursor;

class QttGraphicsItemA : public QObject, public QGraphicsItem
{
    Q_OBJECT
    Q_INTERFACES(QGraphicsItem)
public:

    QttGraphicsItemA(double x, double y, double width, double height);

    QRectF getRect() const;

protected:
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
    virtual QRectF boundingRect() const override;
    void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;


private:
    void rotate(QGraphicsSceneMouseEvent * event);

private:
    // 主框大小,即四边形虚线范围
    QRectF m_rect;
    qreal m_width, m_height;
    qreal m_left, m_top, m_right, m_bottom;
    qreal m_scale;
    QSize minSize;


    QRectF m_leftTopRect, m_rightTopRect, m_rightBottomRect, m_leftBottomRect;

    qreal m_line_rotate = 20;

    QPixmap m_leftTopPix;
    QPixmap m_rightTopPix;
    QPixmap right_BottomPix;
    QPixmap m_leftBottomPix;
    QPixmap m_rotatePix;
    QttGraphicsItemCursor* item_rotate;
    QttGraphicsItemCursor* item_leftTop;
    QttGraphicsItemCursor* item_rightTop;
    QttGraphicsItemCursor* item_rightBottom;
    QttGraphicsItemCursor* item_leftBottom;

    QPointF pPress;

    void init_painter_button();
    //!
    void paint_button(qreal radius);
};

//! 缩放、旋转图标item
//! 用于判断鼠标是否放再特定位置
class QttGraphicsItemCursor : public QObject, public QGraphicsItem
{
    Q_OBJECT
    Q_INTERFACES(QGraphicsItem)
public:
    QttGraphicsItemCursor(double x, double y, double width, double height, Qt::CursorShape type = Qt::ArrowCursor);
    void setItemRect(QRectF rect);
    void setCursorType(Qt::CursorShape type);
    Qt::CursorShape getCursorShape(){return m_cursor_shape;}

    bool isPress();

protected:
    virtual QRectF boundingRect() const override;
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
    void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
    void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
    void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;


private:
    QRectF m_rect;
    Qt::CursorShape m_cursor_shape;
};

#endif // QTTGRAPHICSITEMA_H
#include "qttgraphicsitema.h"
#include <QPainter>
#include <QDebug>
#include <QStyleOptionGraphicsItem>
#include <QDebug>
#include <QImage>
#include <QPolygonF>
#include <QVector2D>
#include <QVector3D>
#include <QtMath>
#include <cmath>

QRectF ImageRect2(QRectF rect)
{
    //图片占主题轮廓的比例0.8
    double x = rect.x();
    double y = rect.y();
    double width = rect.width();
    double height = rect.height();

    double new_x = x+width*0.1;
    double new_y = y+height*0.1;
    double new_w = width*0.8;
    double new_h = height*0.8;

    return QRectF(new_x, new_y, new_w, new_h);
}

double getMargin(QRectF rect)
{
    return rect.width()*0.1 <= rect.height()*0.1 ? rect.width()*0.1 : rect.height()*0.1;
}

QttGraphicsItemA::QttGraphicsItemA(double x, double y, double width, double height) : m_rect(x, y, width, height)
    , m_width(width)
    , m_height(height)
    , m_left(x)
    , m_top(y)
    , m_right(x+width)
    , m_bottom(y+height)
    , m_scale(width/height)
    , minSize(40, 40*m_scale)
{
    this->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);

    this->setAcceptHoverEvents(true);

    m_leftTopPix = QPixmap(":/circle_grey.png");
    m_rightTopPix = QPixmap(":/circle_grey.png");
    right_BottomPix = QPixmap(":/circle_grey.png");
    m_leftBottomPix = QPixmap(":/circle_grey.png");
    m_rotatePix = QPixmap(":/rotate_hover.png");

    item_rotate = nullptr;
    item_leftTop = nullptr;
    item_rightTop = nullptr;
    item_rightBottom = nullptr;
    item_leftBottom = nullptr;

    init_painter_button();
}

void QttGraphicsItemA::init_painter_button()
{
    double radius = getMargin(m_rect)/2;
    if(item_leftBottom == nullptr)
    {
        item_leftBottom = new QttGraphicsItemCursor(m_rect.x()-radius, m_rect.y()+m_rect.height()-radius, radius*2, radius*2, Qt::SizeBDiagCursor);
        item_leftBottom->setCursorType(Qt::CursorShape::SizeBDiagCursor);
        item_leftBottom->setParentItem(this);
        item_leftBottom->setVisible(false);
    }

    if(item_leftTop == nullptr)
    {
        item_leftTop = new QttGraphicsItemCursor(m_rect.x()-radius, m_rect.y()-radius, radius*2, radius*2, Qt::SizeFDiagCursor);
        item_leftTop->setCursorType(Qt::CursorShape::SizeFDiagCursor);
        item_leftTop->setParentItem(this);
        item_leftTop->setVisible(false);
    }

    if(item_rightBottom == nullptr)
    {
        item_rightBottom = new QttGraphicsItemCursor(m_rect.x()+m_rect.width()-radius, m_rect.y()+m_rect.height()-radius, radius*2, radius*2, Qt::CursorShape::SizeFDiagCursor);
        item_rightBottom->setCursorType(Qt::CursorShape::SizeFDiagCursor);
        item_rightBottom->setParentItem(this);
        item_rightBottom->setVisible(false);
    }

    if(item_rightTop == nullptr)
    {
        item_rightTop = new QttGraphicsItemCursor(m_rect.x()+m_rect.width()-radius, m_rect.y()-radius, radius*2, radius*2, Qt::SizeBDiagCursor);
        item_rightTop->setCursorType(Qt::CursorShape::SizeBDiagCursor);
        item_rightTop->setParentItem(this);
        item_rightTop->setVisible(false);
    }

    if(item_rotate == nullptr)
    {
        item_rotate = new QttGraphicsItemCursor(m_rect.x()+m_rect.width()/2-radius, m_rect.y()-m_line_rotate-radius*2, radius*2, radius*2, Qt::SizeAllCursor);
        item_rotate->setCursorType(Qt::CursorShape::SizeAllCursor);
        item_rotate->setParentItem(this);
        item_rotate->setVisible(false);
    }
}


void QttGraphicsItemA::paint_button(qreal radius)
{
    if(item_leftBottom)
    {
        item_leftBottom->setItemRect(QRectF(m_rect.x()-radius, m_rect.y()+m_rect.height()-radius, radius*2, radius*2));
        item_leftBottom->setVisible(true);
    }

    if(item_leftTop)
    {
        item_leftTop->setItemRect(QRectF(m_rect.x()-radius, m_rect.y()-radius, radius*2, radius*2));
        item_leftTop->setVisible(true);
    }

    if(item_rightBottom)
    {
        item_rightBottom->setItemRect(QRectF(m_rect.x()+m_rect.width()-radius, m_rect.y()+m_rect.height()-radius, radius*2, radius*2));
        item_rightBottom->setVisible(true);
    }

    if(item_rightTop)
    {
        item_rightTop->setItemRect(QRectF(m_rect.x()+m_rect.width()-radius, m_rect.y()-radius, radius*2, radius*2));
        item_rightTop->setVisible(true);
    }

    if(item_rotate)
    {
        item_rotate->setItemRect(QRectF(m_rect.x()+m_rect.width()/2-radius, m_rect.y()-m_line_rotate-radius*2, radius*2, radius*2));
        item_rotate->setVisible(true);
    }
}

void QttGraphicsItemA::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
{
    QPixmap pix(":/timg.jpg");
    painter->drawPixmap(ImageRect2(m_rect), pix, pix.rect());

    if(isSelected())
    {
        // 图像轮廓
        QPen pen;
        pen.setStyle(Qt::DashLine);
        pen.setColor(QColor(0,0,255));
        painter->setPen(pen);
        painter->drawRect(m_rect);

        // 5个顶点item
        double radius = getMargin(m_rect)/2;
        paint_button(radius);

        //line
        m_line_rotate = radius*2;
        QPointF start(m_rect.x()+m_rect.width()/2, m_rect.y());
        QPointF end(m_rect.x()+m_rect.width()/2, m_rect.y()-m_line_rotate);
        painter->drawLine(QLineF(start, end));
    }
    else
    {
        if(item_rotate)item_rotate->setVisible(false);
        if(item_leftTop)item_leftTop->setVisible(false);
        if(item_rightTop)item_rightTop->setVisible(false);
        if(item_leftBottom)item_leftBottom->setVisible(false);
        if(item_rightBottom)item_rightBottom->setVisible(false);
    }
}

void QttGraphicsItemA::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if(flags() & ItemIsMovable)
    {
        if ((event->buttons() & Qt::LeftButton))
        {
            QPointF diff = event->pos()-event->lastPos();

            qreal dx = diff.x();
            qreal dy = diff.y();

            QRectF rect = m_rect;


            if(item_rotate->isPress())
            {
                rotate(event);
            }
            else if(item_leftBottom->isPress())
            {
                dx = (rect.width()-dx*2 < minSize.width()) ? (rect.width()-minSize.width())/2 : dx;
                dy = (rect.height()+dy*2 < minSize.height()) ? (minSize.height()- rect.height())/2 : dy;
                rect.adjust(dx, -dy, -dx, dy);
            }
            else if(item_leftTop->isPress())
            {
                dx = (rect.width()-dx*2 < minSize.width()) ? (rect.width()-minSize.width())/2 : dx;
                dy = (rect.height()-dy*2 < minSize.height()) ? (rect.height()-minSize.height())/2 : dy;
                rect.adjust(dx, dy, -dx, -dy);
            }
            else if(item_rightBottom->isPress())
            {
                dx = (rect.width()+dx*2 < minSize.width()) ? (minSize.width()-rect.width())/2 : dx;
                dy = (rect.height()+dy*2 < minSize.height()) ? (minSize.height()- rect.height())/2 : dy;
                rect.adjust(-dx, -dy, dx, dy);
            }
            else if(item_rightTop->isPress())
            {
                dx = (rect.width()+dx*2 < minSize.width()) ? (minSize.width()-rect.width())/2 : dx;
                dy = (rect.height()-dy*2 < minSize.height()) ? (rect.height()-minSize.height())/2 : dy;
                rect.adjust(-dx, dy, dx, -dy);
            }
            else if(getRect().contains(event->pos()))
            {
                //! 此方法在旋转item后移动会抖动,具体原因不了解
                //QPointF diff = event->scenePos() - this->pPress;
                //this->setPos(diff);
                //! 通过计算鼠标在scene中前后两次位置差,移动item
                QPointF diff = event->scenePos() - event->lastScenePos();
                // 不用鼠标在item中坐标是因为当item旋转后计算所得的值不可直接使用
                //QPointF diff2 = event->pos() - event->lastPos();
                this->moveBy(diff.x(),diff.y());
            }

            if(m_rect != rect)
            {
                prepareGeometryChange();
                m_rect = rect;
            }
            this->update();
            this->scene()->update();
        }
    }

    //此处不调用, 影响功能
    //QGraphicsItem::mouseMoveEvent(event);
}

void QttGraphicsItemA::rotate(QGraphicsSceneMouseEvent *event)
{
    QPointF originPos = this->getRect().center();
    // 从原点延伸出去两条线,鼠标按下时的点和当前鼠标位置所在点的连线
    QLineF p1 = QLineF(originPos, pPress);
    QLineF p2 = QLineF(originPos, event->pos());
    // 旋转角度
    qreal dRotateAngle = p2.angleTo(p1);

    // 设置旋转中心
    this->setTransformOriginPoint(originPos);

    // 计算当前旋转的角度
    qreal dCurAngle = this->rotation() + dRotateAngle;
    while (dCurAngle > 360.0) {
        dCurAngle -= 360.0;
    }

    prepareGeometryChange();
    // 设置旋转角度
    this->setRotation(dCurAngle);
}

void QttGraphicsItemA::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    pPress = event->pos();

    QGraphicsItem::mousePressEvent(event);
}

void QttGraphicsItemA::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsItem::mouseReleaseEvent(event);
}


QRectF QttGraphicsItemA::getRect() const
{
    QRectF rect = m_rect;
    if(isSelected() || !isSelected())
    {
        qreal radius = getMargin(rect)/2;
        rect.adjust(-radius, -radius, radius, radius);
        rect.adjust(0, -m_line_rotate-radius, 0, 0);
    }
    return rect;
}

QRectF QttGraphicsItemA::boundingRect() const
{
    QRectF rect = getRect();
    return rect;
}


void QttGraphicsItemA::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
    if(m_rect.contains(event->pos())) setCursor(Qt::OpenHandCursor);

    QGraphicsItem::hoverMoveEvent(event);
}





//!####################################################################################################################################
//!
//!
//!
//!####################################################################################################################################
//! QttGraphicsItemCursor
//!
QttGraphicsItemCursor::QttGraphicsItemCursor(double x, double y, double width, double height, Qt::CursorShape type) : m_rect(x, y, width, height)
    , m_cursor_shape(type)
{
    this->setAcceptHoverEvents(true);
}

void QttGraphicsItemCursor::setItemRect(QRectF rect)
{
    if(rect != m_rect)
    {
        prepareGeometryChange();
        m_rect = rect;
    }
}

void QttGraphicsItemCursor::setCursorType(Qt::CursorShape type)
{
    m_cursor_shape = type;
}

QRectF QttGraphicsItemCursor::boundingRect() const
{
    return m_rect;
}

void QttGraphicsItemCursor::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    switch (m_cursor_shape) {
    case Qt::CursorShape::ArrowCursor:
    {
        QPixmap pix(":/circle_grey.png");
        painter->drawPixmap(/*ImageRect2(m_rect)*/m_rect, pix, pix.rect());
    }break;
    case Qt::CursorShape::OpenHandCursor:
    {
        QPixmap pix(":/circle_grey.png");
        painter->drawPixmap(/*ImageRect2(m_rect)*/m_rect, pix, pix.rect());

    }break;
    case Qt::CursorShape::SizeAllCursor:
    {
        QPixmap pix(":/rotate_hover.png");
        painter->drawPixmap(/*ImageRect2(m_rect)*/m_rect, pix, pix.rect());

    }break;
    case Qt::CursorShape::SizeFDiagCursor:
    {
        QPixmap pix(":/circle_grey.png");
        painter->drawPixmap(/*ImageRect2(m_rect)*/m_rect, pix, pix.rect());

    }break;
    case Qt::CursorShape::SizeBDiagCursor:
    {
        QPixmap pix(":/circle_grey.png");
        painter->drawPixmap(/*ImageRect2(m_rect)*/m_rect, pix, pix.rect());

    }break;
    default:
        break;
    }
}

void QttGraphicsItemCursor::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
    QPointF pos = event->pos();
    if(!m_rect.contains(pos))
    {
        setCursor(Qt::ArrowCursor);
    }
    else
    {
        setCursor(m_cursor_shape);
    }

    QGraphicsItem::hoverMoveEvent(event);
}

void QttGraphicsItemCursor::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsItem::mouseMoveEvent(event);
}

void QttGraphicsItemCursor::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsItem::mousePressEvent(event);
}

void QttGraphicsItemCursor::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsItem::mouseReleaseEvent(event);
}

void QttGraphicsItemCursor::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    setCursor(Qt::ArrowCursor);
    QGraphicsItem::hoverEnterEvent(event);
}
void QttGraphicsItemCursor::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    setCursor(Qt::ArrowCursor);
    QGraphicsItem::hoverLeaveEvent(event);
}

//! 判断设定的鼠标图标样式是否与实际样式一致,是则返回true,否则返回false
bool QttGraphicsItemCursor::isPress()
{
    return this->cursor() == m_cursor_shape;
}

3. 使用

m_scene = new QttGraphicsScene();

// ui->graphicsView 是在ui界面添加的QGraphicsView,然后提升为QttGraphicsView
ui->graphicsView->setScene(scene);

QttGraphicsItemA* item = new QttGraphicsItemA(0,0,200,200);

m_scene->addItem(item);

4.组合

//组合
void Widget::on_pushButton_clicked()
{
    auto items = m_scene->selectedItems();
	if(items.length() <= 0) return;
    QGraphicsItemGroup *itemGroup = new QGraphicsItemGroup;
    itemGroup->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
    scene->addItem(itemGroup);

    foreach (auto item, items) {
        itemGroup->addToGroup(item);
    }
}

//取消组合
void Widget::on_pushButton_2_clicked()
{
    auto items = m_scene->selectedItems();
    if(items.length() != 1) return;
    QGraphicsItemGroup *itemGroup = reinterpret_cast<QGraphicsItemGroup *>(items.at(0));
    itemGroup->removeFromGroup(itemGroup->childItems().first());
    m_scene->destroyItemGroup(itemGroup);
}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值