QGraphicsItem中创建旋转矩形,qt本身坐标系机制引发的旋转中心问题。

#ifndef DRAW_SCENE_H
#define DRAW_SCENE_H
#include<QGraphicsScene>
#include<QGraphicsEllipseItem>
#include<qDebug>
#include<QGraphicsRectItem>
#include<QGraphicsSceneMouseEvent>
#include<QPainter>
#include<QVector2D>
#include<QVector3D>
#include <QtMath>
#include<QStyleOptionGraphicsItem>
#include<QPointF>

#define PI 3.14159265358979


class draw_rect_item :public QObject,public QGraphicsItem
{
    Q_OBJECT
public:
    draw_rect_item(QGraphicsItem *parent = Q_NULLPTR);
    ~draw_rect_item();

public:
    enum MOUSEHANDLE
    {
        handleNone =0,
        handleTopLeft =1,
        handleTopMiddle=2,
        handleTopRight =3,
        handleBottomLeft =4,
        handleBottomMiddle=5,
        handleBottomRight =6,
        handleMiddleLeft =7,
        handleMiddleRight =8,
    } ;

    enum ItemOperator
    {
        t_none=0,
        t_move=1,
        t_resize=2,
        t_rotate=3,
        t_painter=4,
    } ;
    QRectF itemRect ;                          //item的轮廓
private:
    ItemOperator m_itemOper = t_none;
    MOUSEHANDLE m_bhandleSelected=MOUSEHANDLE::handleNone ;    //resize操作,时按压状态

    std::map<MOUSEHANDLE,QRectF>m_handles;     //item中resize操作中的九个矩形
    QRectF outLineRect;                        //item的外轮廓
    static QImage m_rotateIcon;
    QPixmap m_rotatepixmap;

private:
    QPointF m_pos;              // 本地所坐标点击的点
    QPointF m_pressedPos;       // 场景坐标点击的点
    QPointF m_startPos;         // Item在场景坐标的起始坐标
    QTransform m_transform;
    qreal m_rotate=0.0;                        //旋转角度

    QPointF m_rotateCenter=QPointF(0,0);
    QPointF m_rectCenterOffset;
private:
    bool m_isResizeable =true;
    bool m_isRatioScale =true;
    qreal m_rationValue=1.0;

    QSize m_size;
    const float m_frotateTolerance=0;
    qreal m_nInterval=0;
    qreal m_nEllipseWidth =10;
private:
    draw_rect_item::MOUSEHANDLE handleAt(const QPointF& point); //得到鼠标位置处的状态
    void initIcon();
    void updateHandlesPos(); //调整四周小矩形的位置和大小。
    QCursor getRotateCursor(const qreal angle) ;  //旋转光标

protected:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = Q_NULLPTR);
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
    QRectF boundingRect() const override; //响应item事件的边界的一个函数


private:
    virtual void  mouseMoveRotateOperator(const QPointF rotateStartPos, const QPointF rotateEndPos, const QPointF rotateCenterPos);
    virtual void  mouseMoveResizeOperator(draw_rect_item::MOUSEHANDLE dir, const QPointF& loacalPos);
    virtual void mouseMoveMoveOperator(const QPointF& localpos);

};









#include<QGraphicsSceneMouseEvent>
#include<QMouseEvent>
class draw_scene:public QGraphicsScene
{
public:
    enum ItemOperator
    {
        t_none,
        t_move,
        t_resize,
        t_rotate,
        t_painter
    };

private:
    ItemOperator m_itemOper = t_none;
    QPointF m_startPos;         // Item在场景坐标的起始坐标
    QPointF m_endPos;         // Item在场景坐标的结束坐标
    QRectF* rectF =NULL;
public:
    explicit draw_scene();

private:
    //QGraphicsSimpleTextItem* rect_text_Item;
    //QGraphicsLineItem rect_line_Item;
    QGraphicsRectItem* rect_item;

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void keyPressEvent(QKeyEvent * event)
    {
        qDebug() << "Custom scene keyPressEvent.";
        QGraphicsScene::keyPressEvent(event);
    }

private:
    virtual void mouseMovepaintOperator(const QPointF& scenePos ,const QPointF& localpos);
};




#endif // DRAW_SCENE_H
#include "draw_scene.h"

QImage draw_rect_item::m_rotateIcon;







draw_rect_item::draw_rect_item(QGraphicsItem *parent)
    : QGraphicsItem(parent)
{

    setAcceptHoverEvents(true);
        //这里不可以把标志设为可移动
    //this->setFlags( QGraphicsItem::ItemIsFocusable| QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);
    this->setFlag(QGraphicsItem::ItemIsSelectable, true);
    initIcon();

}

void draw_rect_item::initIcon()
{
    //初始化变量
    m_size = QSize(500,100);
    m_handles.insert(std::make_pair(MOUSEHANDLE::handleTopLeft,QRectF()));
    m_handles.insert(std::make_pair(MOUSEHANDLE::handleTopMiddle,QRectF()));
    m_handles.insert(std::make_pair(MOUSEHANDLE::handleTopRight,QRectF()));
    m_handles.insert(std::make_pair(MOUSEHANDLE::handleBottomLeft,QRectF()));
    m_handles.insert(std::make_pair(MOUSEHANDLE::handleBottomMiddle,QRectF()));
    m_handles.insert(std::make_pair(MOUSEHANDLE::handleBottomRight,QRectF()));
    m_handles.insert(std::make_pair(MOUSEHANDLE::handleMiddleLeft,QRectF()));
    m_handles.insert(std::make_pair(MOUSEHANDLE::handleMiddleRight,QRectF()));

    QPointF centerPos(m_rotateCenter);//item项的中心,为原点
    itemRect = QRectF(centerPos.x()-m_size.width()/2,centerPos.y()-m_size.height()/2,m_size.width(),m_size.height());   //本地坐标系下,矩形左上角的位置。根据本地坐标系,判断鼠标点击的位置

    outLineRect =itemRect.adjusted(-m_nInterval,-m_nInterval,m_nInterval,m_nInterval);//画圆之前,把outline矩形的中心=圆的外切矩形中心重合


}


draw_rect_item::~draw_rect_item()
{

}



//得到鼠标处,鼠标的状态标志
draw_rect_item::MOUSEHANDLE draw_rect_item::handleAt(const QPointF &point)
{
    for (auto it : m_handles)
    {
        if (it.second.contains(point))
        {
            qDebug()<< "it.first"<<it.first;
            return it.first;

        }
    }
    qDebug()<< "it.first  MOUSEHANDLE::handleNone";
    return MOUSEHANDLE::handleNone;

}

void draw_rect_item::mouseMoveResizeOperator(draw_rect_item::MOUSEHANDLE dir, const QPointF &localPos)
{
    //itemRect;  //返回当前的矩形  outLineRect;//返回当前矩形的外边框矩形
    switch (dir)
    {
    case MOUSEHANDLE::handleTopLeft:
        itemRect.setTopLeft(localPos.toPoint());   //本地坐标系上,item的坐标
        break;
    case MOUSEHANDLE::handleTopMiddle:
        itemRect.setTop(localPos.y());
        qDebug()<<m_rotate;
        this->setCursor(getRotateCursor(m_rotate));
        break;
    case MOUSEHANDLE::handleTopRight:
        itemRect.setTopRight(localPos);
        break;
    case MOUSEHANDLE::handleBottomLeft:
        itemRect.setBottomLeft(localPos);
        break;
    case MOUSEHANDLE::handleBottomMiddle:
        itemRect.setBottom(localPos.y());
        break;
    case MOUSEHANDLE::handleBottomRight:
        itemRect.setBottomRight(localPos);
        break;
    case MOUSEHANDLE::handleMiddleLeft:
        itemRect.setLeft(localPos.x());
        break;
    case MOUSEHANDLE::handleMiddleRight:
        itemRect.setRight(localPos.x());
        break;
    default:
        this->setCursor(Qt::ArrowCursor);
        break;
    }
    qDebug()<<"itemRect_center"<<itemRect.center();

}


//重载-。qpointf可以和qreal类型相加
QPointF operator-(const QPointF& pointF ,qreal value)
{
    return QPointF(pointF.x() - value, pointF.y() - value);
}

void draw_rect_item::updateHandlesPos()
{
    outLineRect =itemRect.adjusted(-m_nInterval,-m_nInterval,m_nInterval,m_nInterval);//画圆之前,把outline矩形的中心=圆的外切矩形中心重合
    QSize EllipseWidth=QSize(m_nEllipseWidth,m_nEllipseWidth);
    m_handles[MOUSEHANDLE::handleTopLeft] = QRectF(outLineRect.topLeft()- (m_nEllipseWidth/2),EllipseWidth);
    m_handles[MOUSEHANDLE::handleTopMiddle] = QRectF(QPoint(outLineRect.center().x(),outLineRect.top()- (m_nEllipseWidth/2)),EllipseWidth);
    m_handles[MOUSEHANDLE::handleTopRight] = QRectF(outLineRect.topRight()- m_nEllipseWidth/2,EllipseWidth);
    m_handles[MOUSEHANDLE::handleBottomLeft] = QRectF(outLineRect.bottomLeft()- m_nEllipseWidth/2,EllipseWidth);
    m_handles[MOUSEHANDLE::handleBottomMiddle] = QRectF(QPoint(outLineRect.center().x(),outLineRect.bottom()- (m_nEllipseWidth/2)),EllipseWidth);
    m_handles[MOUSEHANDLE::handleBottomRight] = QRectF(outLineRect.bottomRight()- m_nEllipseWidth/2,EllipseWidth);
    m_handles[MOUSEHANDLE::handleMiddleLeft] = QRectF(QPoint(outLineRect.left()- (m_nEllipseWidth/2),outLineRect.center().y()),EllipseWidth);
    m_handles[MOUSEHANDLE::handleMiddleRight] = QRectF(QPoint(outLineRect.right()- (m_nEllipseWidth/2),outLineRect.center().y()),EllipseWidth);
}

//重载+。qpointf可以和qreal类型相加
QPointF operator+(const QPointF& point, qreal value)
{
    return QPointF(point.x() + value, point.y() + value);
}

//重载+。qreal可以和qpointf类型相加
QPointF operator+(qreal value, const QPointF& point)
{
    return QPointF(point.x() + value, point.y() + value);
}

QCursor draw_rect_item::getRotateCursor(const qreal angle)
{
    if(m_rotateIcon.isNull())
    {
        m_rotateIcon.load("C:/Users/dell/Pictures/Camera Roll/22.jpg");
    }
    m_rotatepixmap=QPixmap::fromImage(m_rotateIcon);


    QPainter* painter;
    painter->drawPixmap(QRect(outLineRect.topRight().x() - m_nEllipseWidth / 2, outLineRect.topRight().y() - m_nEllipseWidth / 2, m_nEllipseWidth, m_nEllipseWidth), m_rotatepixmap);


    QTransform transform;
    transform.rotate(angle);
    QPixmap rotatedPixmap = m_rotatepixmap.transformed(transform);

    return QCursor(rotatedPixmap);


}
//绘制item时,主要时通过item本地坐标系。实现改变m_size的大小,距离本地坐标系的原点,进行缩放,旋转,
void draw_rect_item::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{

    QPen pen= painter->pen();
    pen.setStyle(Qt::DashLine);
    painter->setPen(pen);

    painter->drawRect(itemRect);

    if (!this->isSelected())
        return;
    painter->drawRect(outLineRect);
    painter->setPen(Qt::NoPen);
    painter->setBrush(Qt::red);
    updateHandlesPos();       //矩形上九点圆形的外接矩形
    for (auto it : m_handles)
    {
        painter->drawEllipse(it.second);
    }

    painter->setBrush(Qt::blue);
    painter->drawEllipse(boundingRect());

    painter->setBrush(Qt::black);
    painter->drawEllipse(QRectF(pos(),QSize(10,10)));

    painter->setBrush(Qt::green);
    painter->drawEllipse(QRectF(  transformOriginPoint(),QSize(10,10)));


}



void draw_rect_item::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QPointF scenepos= event->scenePos();    //获取场景坐标
    QPointF pos=event->pos();

    m_pos=pos;           //旋转的起始点
    m_pressedPos=scenepos;
    m_startPos=this->pos();

    m_bhandleSelected= handleAt(pos);      //返回mousehandle的状态
    if(m_bhandleSelected==MOUSEHANDLE::handleNone)
    {
        m_itemOper=t_rotate;
        // m_itemOper = t_move;
    }
    else
    {
        m_itemOper= t_resize;
    }

    return QGraphicsItem::mousePressEvent(event);  // 调用基类的处理

}


void draw_rect_item::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{

    QPointF sceneMovePos=event->scenePos(); //场景坐标
    QPointF localPos=event->pos();      //图像项坐标(一般位图形项的中心位原点)

    QPointF pt1,pt2,delta;
    pt1 = transformOriginPoint();
    pt2 = boundingRect().center();
    delta = pt1 - pt2;
    setTransform(transform().translate(delta.x(),delta.y()));
    setTransformOriginPoint(boundingRect().center());
    moveBy(-delta.x(),-delta.y());

    if(m_itemOper == t_move )
    {
        mouseMoveMoveOperator(sceneMovePos);//移动ok
    }

    if(m_itemOper ==t_resize)
    {
        //当我们设置了新的中心点,如果已经有了旋转角度的话,那么则会出现平移的现象
        mouseMoveResizeOperator(m_bhandleSelected,localPos); //放大缩小操作ok
    }
    else if(m_itemOper == t_rotate)
    {
        // QPointF delta = pos() - boundingRect().center() ; // 将本地坐标原点移动到中心,



        qDebug()<<"start================";
        qDebug()<<"pos-boundingRect.center="<<delta;
        qDebug()<<"op"<<pos();
        qDebug()<<boundingRect().center();

        m_rotateCenter=boundingRect().center();
        //当再次旋转时,item进行重绘,重绘坐标原点仍然是原来的pos()坐标。重绘时先绘制图形,然后应用该图形的transform。而rect.center()的坐标已经不再是缩放时的center,所以会发生图形漂移
        mouseMoveRotateOperator(m_pressedPos, sceneMovePos,m_rotateCenter); //旋转图形,仍以局部坐标为准。
    }
    this->update();
    QGraphicsItem::mouseMoveEvent(event);
}

void draw_rect_item::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{

    qDebug()<<"QGraphicsrectItem_release";
    QGraphicsItem::mouseReleaseEvent(event);
}

QRectF draw_rect_item::boundingRect() const
{

    QRectF boundingToleranceRect= outLineRect.adjusted(-m_frotateTolerance,-m_frotateTolerance,m_frotateTolerance,m_frotateTolerance);//旋转矩形的容忍度
    // if(!this->isSelected())
    //     return rectF;
    return boundingToleranceRect;

}


//旋转中心=scene中的坐标系(点)。
//旋转起点=scene中的坐标系(点)。
//旋转终点=scene中的坐标系(点)。 用scene中的坐标系,解决拖影
//刚开始0度时。旋转中心不会变,如果矩形
void draw_rect_item::mouseMoveRotateOperator(const QPointF rotateStartPos, const QPointF rotateEndPos, const QPointF rotateCenterPos)
{
    QVector2D startVec(rotateStartPos.x() - rotateCenterPos.x()  ,rotateStartPos.y()- rotateCenterPos.y());  //(0,0)为本地坐标系的原点
    startVec.normalize();
    QVector2D endVec(rotateEndPos.x()- rotateCenterPos.x() ,rotateEndPos.y()- rotateCenterPos.y());
    endVec.normalize();//获取单位向量(向量/模长)

    qreal dotValue = QVector2D::dotProduct(startVec,endVec); //单位向量点乘,等于cos0

    if (dotValue > 1.0)
        dotValue = 1.0;
    else if (dotValue < -1.0)
        dotValue = -1.0;

    dotValue=qAcos(dotValue);
    if(isnan(dotValue))
    {
        dotValue=0.0;
    }
    qreal angle =dotValue*1.0/(PI/180); //获取角度

    QVector3D crossValue =QVector3D::crossProduct(QVector3D(startVec,1.0),QVector3D(endVec,1.0));//向量叉乘获取角度
    qDebug()<<crossValue.z()<<"crossValue.z()";
    if(crossValue.z()<0)
    {
        angle=-angle;
    }

    m_rotate+=angle;

    setPos(rotateCenterPos);


    setTransformOriginPoint(rotateCenterPos);
    setRotation(m_rotate);
    m_pressedPos=rotateEndPos;

}

void draw_rect_item::mouseMoveMoveOperator(const QPointF &scenePos)
{
    qreal xInterVal =scenePos.x() - m_pressedPos.x();
    qreal yInterVal =scenePos.y() - m_pressedPos.y();
    this->setPos(m_startPos +QPointF(xInterVal,yInterVal));//设置在场景中位置
    this->update();
}







draw_scene::draw_scene()
{

}

void draw_scene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mousePressEvent(event);
    qDebug()<<"scene_press";
}

void draw_scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{


    QGraphicsScene::mouseReleaseEvent(event);

}

void draw_scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{

    QGraphicsScene::mouseMoveEvent(event);

}

void draw_scene::mouseMovepaintOperator(const QPointF &scenePos, const QPointF &localpos)
{
    qDebug()<<rectF;
    QLineF line(m_startPos,scenePos);
    rectF->setBottomRight(scenePos);

    rectF=&rectF->normalized();


}


问题一:图形项,在修改之后还是会漂移。表现为拉伸之后,旋转。图像项漂移。

资源链接https://download.csdn.net/download/qq_57480369/89767831

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值