Qt 通过重写QGraphicItem实现绘制、拖动、缩放、旋转矩形

Qt 通过重写QGraphicItem实现绘制、拖动、缩放、旋转矩形
本例程通过重写了一个类,继承自QGraphicItem,来实现了在qgraphicsScene上绘制、拖动、缩放、旋转矩形。
效果如下:
在这里插入图片描述

其实要实现绘制、拖动、缩放矩形都不难,难的是在旋转之后还要支持缩放。
我的思路是:
1.实现绘制矩形:只要定义一个全局变量QRectF m_oldRect,在外面矩形大小传进来,然后在paint函数里面绘制这个矩形就行
2.实现拖动矩形:重写mousePressEvent,mouseMoveEvent,mouseReleaseEvent,记录鼠标按下的起始点和移动时候的点,并用moveBy()函数来移动矩形即可
3.实现缩放:在矩形内部靠近4条边的地方定义4个矩形,当鼠标按下的时候在这4个矩形方框内,则将矩形往4个方向拉伸
4.实现旋转:
我给之前定义的矩形全部配一个QPolygonF,因为我只能通过绘制多边形的方式来画出旋转之后的矩形。
矩形正上方的圆圈我是通过三角函数+直线方程来计算,让其始终绘制在矩形左右两个顶点的中垂线上方。
当鼠标落在圆形内部的时候可以控制矩形旋转。
在矩形旋转之后,再进行拉伸的时候,我是通过的计算鼠标的点离对面的边的距离来重新计算矩形的大小,然后计算对应的QPolygonF的大小。

主要代码如下:

mygraphicrectitem.h

#ifndef MYGRAPHICRECTITEM_H
#define MYGRAPHICRECTITEM_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
enum STATE_FLAG{
DEFAULT_FLAG=0,
MOV_LEFT_LINE,//标记当前为用户按下矩形的左边界区域
MOV_TOP_LINE,//标记当前为用户按下矩形的上边界区域
MOV_RIGHT_LINE,//标记当前为用户按下矩形的右边界区域
MOV_BOTTOM_LINE,//标记当前为用户按下矩形的下边界区域
MOV_RIGHTBOTTOM_RECT,//标记当前为用户按下矩形的右下角
MOV_RECT,//标记当前为鼠标拖动图片移动状态
ROTATE//标记当前为旋转状态
};
class myGraphicRectItem:public QObject,public QGraphicsItem
{
Q_OBJECT
public:
myGraphicRectItem(QGraphicsItem *parent = nullptr);
//myGraphicRectItem(QRectF m_OriginRect = QRectF(0,0,100,100));
QRectF boundingRect() const;
~myGraphicRectItem();
void setRectSize(QRectF mrect,bool bResetRotateCenter = true);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void SetRotate(qreal RotateAngle,QPointF ptCenter=QPointF(-999,-999));
QPointF getRotatePoint(QPointF ptCenter, QPointF ptIn, qreal angle);//获取旋转后的点
QList getRotatePoints(QPointF ptCenter,QList ptIns,qreal angle);//获取多个旋转后的点
QPolygonF getRotatePolygonFromRect(QPointF ptCenter,QRectF rectIn,qreal angle);//将矩形旋转之后返回多边形
QRectF getCrtPosRectToSceen();
QRectF m_SmallRotateRect;//矩形顶部用来表示旋转的标记的矩形
QPolygonF m_SmallRotatePolygon;//矩形顶部用来表示旋转的标记的矩形旋转后形成的多边形
QPointF getSmallRotateRectCenter(QPointF ptA,QPointF ptB);//获取旋转时候矩形正上方的旋转标记矩形
QRectF getSmallRotateRect(QPointF ptA,QPointF ptB);
bool m_bRotate;
qreal m_RotateAngle;
QPointF m_RotateCenter;

private:
QRectF m_oldRect;
QPolygonF m_oldRectPolygon;
QRectF m_RotateAreaRect;
bool m_bResize;
QPolygonF m_insicedPolygon;
QRectF m_insicedRectf;
QPolygonF m_leftPolygon;
QRectF m_leftRectf;
QPolygonF m_topPolygon;
QRectF m_topRectf;
QPolygonF m_rightPolygon;
QRectF m_rightRectf;
QPolygonF m_bottomPolygon;
QRectF m_bottomRectf;
// QPolygonF m_rbPolygon;
// QRectF m_rbRectf;
QPointF m_startPos;
STATE_FLAG m_StateFlag;
QPointF *pPointFofSmallRotateRect;
protected:

};
#endif // MYGRAPHICRECTITEM_H

mygraphicrectitem.cpp

#include “mygraphicrectitem.h”
#include
#include

myGraphicRectItem::myGraphicRectItem(QGraphicsItem *parent):
m_bResize(false),
m_oldRect(0,0,100,100),
m_bRotate(false),
m_RotateAngle(0),
m_StateFlag(DEFAULT_FLAG)
{
//setParent(parent);
setRectSize(m_oldRect);
setToolTip(“Click and drag me!”); //提示
setCursor(Qt::ArrowCursor); //改变光标形状,手的形状
setFlag(QGraphicsItem::ItemIsMovable);
// setAcceptDrops(true);
pPointFofSmallRotateRect = new QPointF[4];
SetRotate(0);
setFlag(QGraphicsItem::ItemIsSelectable);//
}

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

myGraphicRectItem::~myGraphicRectItem()
{
delete []pPointFofSmallRotateRect;
pPointFofSmallRotateRect = nullptr;
}

void myGraphicRectItem::setRectSize(QRectF mrect, bool bResetRotateCenter)
{
m_oldRect = mrect;
if(bResetRotateCenter)
{
m_RotateCenter.setX(m_oldRect.x()+m_oldRect.width()/2);
m_RotateCenter.setY(m_oldRect.y()+m_oldRect.height()/2);
}
m_oldRectPolygon = getRotatePolygonFromRect(m_RotateCenter,m_oldRect,m_RotateAngle);

m_insicedRectf = QRectF(m_oldRect.x()+8,m_oldRect.y()+8,m_oldRect.width()-16,m_oldRect.height()-16);
m_insicedPolygon =getRotatePolygonFromRect(m_RotateCenter,m_insicedRectf,m_RotateAngle);

m_leftRectf = QRectF(m_oldRect.x(),m_oldRect.y(),8,m_oldRect.height()-8);
m_leftPolygon = getRotatePolygonFromRect(m_RotateCenter,m_leftRectf,m_RotateAngle);

m_topRectf = QRectF(m_oldRect.x()+8,m_oldRect.y(),m_oldRect.width()-8,8);
m_topPolygon = getRotatePolygonFromRect(m_RotateCenter,m_topRectf,m_RotateAngle);

m_rightRectf = QRectF(m_oldRect.right()-8,m_oldRect.y()+8,8,m_oldRect.height()-16);
m_rightPolygon = getRotatePolygonFromRect(m_RotateCenter,m_rightRectf,m_RotateAngle);

m_bottomRectf = QRectF(m_oldRect.x(),m_oldRect.bottom()-8,m_oldRect.width()-8,8);
m_bottomPolygon = getRotatePolygonFromRect(m_RotateCenter,m_bottomRectf,m_RotateAngle);

// m_rbRectf = QRectF(m_oldRect.right()-8,m_oldRect.bottom()-8,8,8);
// m_rbPolygon = getRotatePolygonFromRect(m_RotateCenter,m_rbRectf,m_RotateAngle);

m_SmallRotateRect = getSmallRotateRect(mrect.topLeft(),mrect.topRight());//矩形正上方的旋转标记矩形
m_SmallRotatePolygon = getRotatePolygonFromRect(m_RotateCenter,m_SmallRotateRect,m_RotateAngle);

}

void myGraphicRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QPen mPen = QPen(Qt::yellow);
painter->setPen(mPen);
//绘制旋转后的矩形
painter->drawPolygon(m_oldRectPolygon);
//绘制旋转圆形
mPen.setWidth(2);
mPen.setColor(Qt::green);
painter->setPen(mPen);
QPointF pf = getSmallRotateRectCenter(m_oldRectPolygon[0],m_oldRectPolygon[1]);
QRectF rect = QRectF(pf.x()-10,pf.y()-10,20,20);
painter->drawEllipse(rect);//绘制圆形
painter->drawPoint(pf);//绘制点
}

void myGraphicRectItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button()== Qt::LeftButton)
{
m_startPos = event->pos();//鼠标左击时,获取当前鼠标在图片中的坐标,
if(m_SmallRotatePolygon.containsPoint(m_startPos,Qt::WindingFill))//旋转矩形
{
setCursor(Qt::PointingHandCursor);
m_StateFlag = ROTATE;
}
else if(m_insicedPolygon.containsPoint(m_startPos,Qt::WindingFill))//在矩形内框区域时按下鼠标,则可拖动图片
{
setCursor(Qt::ClosedHandCursor); //改变光标形状,手的形状
m_StateFlag = MOV_RECT;//标记当前为鼠标拖动图片移动状态
}
else if(m_leftPolygon.containsPoint(m_startPos,Qt::WindingFill))
{
setCursor(Qt::SizeHorCursor);
m_StateFlag = MOV_LEFT_LINE;//标记当前为用户按下矩形的左边界区域
}
else if(m_rightPolygon.containsPoint(m_startPos,Qt::WindingFill))
{
setCursor(Qt::SizeHorCursor);
m_StateFlag = MOV_RIGHT_LINE;//标记当前为用户按下矩形的右边界区域
}
else if(m_topPolygon.containsPoint(m_startPos,Qt::WindingFill))
{
setCursor(Qt::SizeVerCursor);
m_StateFlag = MOV_TOP_LINE;//标记当前为用户按下矩形的上边界区域
}
else if(m_bottomPolygon.containsPoint(m_startPos,Qt::WindingFill))
{
setCursor(Qt::SizeVerCursor);
m_StateFlag = MOV_BOTTOM_LINE;//标记当前为用户按下矩形的下边界区域
}
// else if(m_rbPolygon.containsPoint(m_startPos,Qt::WindingFill))
// {
// setCursor(Qt::SizeFDiagCursor);
// m_StateFlag = MOV_RIGHTBOTTOM_RECT;//标记当前为用户按下矩形的右下角
// }
else
{
m_StateFlag = DEFAULT_FLAG;
}
}
else
{
QGraphicsItem::mousePressEvent(event);
}
}

void myGraphicRectItem::mouseMoveEvent(QGraphicsSceneMouseEvent event)
{
if(m_StateFlag == ROTATE)
{
int nRotateAngle = atan2((event->pos().x()-m_RotateCenter.x()),(event->pos().y()-m_RotateCenter.y()))180/M_PI;
SetRotate(180-nRotateAngle);
setRectSize(m_oldRect);
//qDebug()<<nRotateAngle;
}
else if(m_StateFlag == MOV_RECT)
{
QPointF point = (event->pos() - m_startPos);
moveBy(point.x(), point.y());
setRectSize(m_oldRect);
scene()->update();
}
else if(m_StateFlag == MOV_LEFT_LINE)
{
QPointF pf = QPointF((m_oldRectPolygon.at(1).x()+m_oldRectPolygon.at(2).x())/2,((m_oldRectPolygon.at(1).y()+m_oldRectPolygon.at(2).y())/2));
//计算到右侧边中点的距离
qreal dis = sqrt((event->pos().x()-pf.x())
(event->pos().x()-pf.x()) +(event->pos().y()-pf.y())
(event->pos().y()-pf.y()));
qreal dis2LT = sqrt((event->pos().x()-m_oldRectPolygon.at(0).x())(event->pos().x()-m_oldRectPolygon.at(0).x()) +(event->pos().y()-m_oldRectPolygon.at(0).y())(event->pos().y()-m_oldRectPolygon.at(0).y()));
qreal dis2RT = sqrt((event->pos().x()-m_oldRectPolygon.at(1).x())(event->pos().x()-m_oldRectPolygon.at(1).x()) +(event->pos().y()-m_oldRectPolygon.at(1).y())(event->pos().y()-m_oldRectPolygon.at(1).y()));
if(dis<16||dis2LT>dis2RT)
{
return;
}
else
{
QRectF newRect(m_oldRect);
newRect.setLeft(m_oldRect.right()-dis);
newRect.setRight(m_oldRect.right());
setRectSize(newRect,false);
m_RotateCenter=QPointF((m_oldRectPolygon.at(0).x()+m_oldRectPolygon.at(2).x())/2,(m_oldRectPolygon.at(0).y()+m_oldRectPolygon.at(2).y())/2);
m_oldRect.moveCenter(m_RotateCenter);
setRectSize(m_oldRect);
scene()->update();//必须要用scene()->update(),不能用update();否则会出现重影
}
}
else if(m_StateFlag == MOV_TOP_LINE)
{
//底边中点
QPointF pf = QPointF((m_oldRectPolygon.at(2).x()+m_oldRectPolygon.at(3).x())/2,((m_oldRectPolygon.at(2).y()+m_oldRectPolygon.at(3).y())/2));
//计算到底边中点的距离
qreal dis = sqrt((event->pos().x()-pf.x())(event->pos().x()-pf.x()) +(event->pos().y()-pf.y())(event->pos().y()-pf.y()));
qreal dis2LT = sqrt((event->pos().x()-m_oldRectPolygon.at(0).x())(event->pos().x()-m_oldRectPolygon.at(0).x()) +(event->pos().y()-m_oldRectPolygon.at(0).y())(event->pos().y()-m_oldRectPolygon.at(0).y()));
qreal dis2LB = sqrt((event->pos().x()-m_oldRectPolygon.at(3).x())(event->pos().x()-m_oldRectPolygon.at(3).x()) +(event->pos().y()-m_oldRectPolygon.at(3).y())(event->pos().y()-m_oldRectPolygon.at(3).y()));
if(dis<16||dis2LT>dis2LB)
{
return;
}
else
{
QRectF newRect(m_oldRect);
newRect.setTop(m_oldRect.bottom()-dis);
newRect.setBottom(m_oldRect.bottom());
setRectSize(newRect,false);
m_RotateCenter=QPointF((m_oldRectPolygon.at(0).x()+m_oldRectPolygon.at(2).x())/2,(m_oldRectPolygon.at(0).y()+m_oldRectPolygon.at(2).y())/2);
m_oldRect.moveCenter(m_RotateCenter);
setRectSize(m_oldRect);
scene()->update();//必须要用scene()->update(),不能用update();否则会出现重影
}
}
else if(m_StateFlag == MOV_RIGHT_LINE)
{
QPointF pf = QPointF((m_oldRectPolygon.at(0).x()+m_oldRectPolygon.at(3).x())/2,((m_oldRectPolygon.at(0).y()+m_oldRectPolygon.at(3).y())/2));
//计算到左侧边中点的距离
qreal dis = sqrt((event->pos().x()-pf.x())(event->pos().x()-pf.x()) +(event->pos().y()-pf.y())(event->pos().y()-pf.y()));
qreal dis2LT = sqrt((event->pos().x()-m_oldRectPolygon.at(0).x())(event->pos().x()-m_oldRectPolygon.at(0).x()) +(event->pos().y()-m_oldRectPolygon.at(0).y())(event->pos().y()-m_oldRectPolygon.at(0).y()));
qreal dis2RT = sqrt((event->pos().x()-m_oldRectPolygon.at(1).x())(event->pos().x()-m_oldRectPolygon.at(1).x()) +(event->pos().y()-m_oldRectPolygon.at(1).y())(event->pos().y()-m_oldRectPolygon.at(1).y()));
if(dis<16||dis2LT<dis2RT)
{
return;
}
else
{
QRectF newRect(m_oldRect);
newRect.setLeft(m_oldRect.left());
newRect.setRight(m_oldRect.left()+dis);
setRectSize(newRect,false);
m_RotateCenter=QPointF((m_oldRectPolygon.at(0).x()+m_oldRectPolygon.at(2).x())/2,(m_oldRectPolygon.at(0).y()+m_oldRectPolygon.at(2).y())/2);
m_oldRect.moveCenter(m_RotateCenter);
setRectSize(m_oldRect);
scene()->update();//必须要用scene()->update(),不能用update();否则会出现重影
}
}
else if(m_StateFlag == MOV_BOTTOM_LINE)
{
//顶边中点
QPointF pf = QPointF((m_oldRectPolygon.at(0).x()+m_oldRectPolygon.at(1).x())/2,((m_oldRectPolygon.at(0).y()+m_oldRectPolygon.at(1).y())/2));
//计算到底边中点的距离
qreal dis = sqrt((event->pos().x()-pf.x())(event->pos().x()-pf.x()) +(event->pos().y()-pf.y())(event->pos().y()-pf.y()));
qreal dis2LT = sqrt((event->pos().x()-m_oldRectPolygon.at(0).x())(event->pos().x()-m_oldRectPolygon.at(0).x()) +(event->pos().y()-m_oldRectPolygon.at(0).y())(event->pos().y()-m_oldRectPolygon.at(0).y()));
qreal dis2LB = sqrt((event->pos().x()-m_oldRectPolygon.at(3).x())(event->pos().x()-m_oldRectPolygon.at(3).x()) +(event->pos().y()-m_oldRectPolygon.at(3).y())(event->pos().y()-m_oldRectPolygon.at(3).y()));
if(dis<16||dis2LT<dis2LB)
{
return;
}
else
{
QRectF newRect(m_oldRect);
newRect.setTop(m_oldRect.top());
newRect.setBottom(m_oldRect.top()+dis);
setRectSize(newRect,false);
m_RotateCenter=QPointF((m_oldRectPolygon.at(0).x()+m_oldRectPolygon.at(2).x())/2,(m_oldRectPolygon.at(0).y()+m_oldRectPolygon.at(2).y())/2);
m_oldRect.moveCenter(m_RotateCenter);
setRectSize(m_oldRect);
scene()->update();//必须要用scene()->update(),不能用update();否则会出现重影
}
}
}

void myGraphicRectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
setCursor(Qt::ArrowCursor);
if(m_StateFlag == MOV_RECT)
{
m_StateFlag = DEFAULT_FLAG;
}
else {
QGraphicsItem::mouseReleaseEvent(event);
}
}

void myGraphicRectItem::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();
}

QPointF myGraphicRectItem::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(angleM_PI/180)-(y-dy)sin(angleM_PI/180)+dx;
yy = (x-dx)sin(angleM_PI/180)+(y-dy)cos(angleM_PI/180)+dy;

return QPointF(xx,yy);

}

QList myGraphicRectItem::getRotatePoints(QPointF ptCenter, QList ptIns, qreal angle)
{
QList lstPt;
for(int i = 0;i<ptIns.count();i++)
{
lstPt.append(getRotatePoint(ptCenter,ptIns.at(i),angle));
}
return lstPt;
}

QPolygonF myGraphicRectItem::getRotatePolygonFromRect(QPointF ptCenter, QRectF rectIn, qreal angle)
{
QVector 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);
}

QRectF myGraphicRectItem::getCrtPosRectToSceen()
{
QRectF retRect = QRectF(m_oldRect.x()+pos().x(),m_oldRect.y()+pos().y(),m_oldRect.width(),m_oldRect.height());
return retRect;
}
QRectF myGraphicRectItem::getSmallRotateRect(QPointF ptA,QPointF ptB)
{
QPointF pt = getSmallRotateRectCenter(ptA,ptB);
return QRectF(pt.x()-10,pt.y()-10,20,20);
}
QPointF myGraphicRectItem::getSmallRotateRectCenter(QPointF ptA,QPointF ptB)
{
QPointF ptCenter = QPointF((ptA.x()+ptB.x())/2,(ptA.y()+ptB.y())/2);//A,B点的中点C
//中垂线方程式为 y=xk + b;
qreal x,y;//旋转图标矩形的中心
if(abs(ptB.y()-ptA.y())<0.1)
{
if(ptA.x()<ptB.x())//矩形左上角在上方
{
x = ptCenter.x();
y = ptCenter.y()-20;
}
else//矩形左上角在下方
{
x = ptCenter.x();
y = ptCenter.y()+20;
}
}
else if(ptB.y()>ptA.y())//顺时针旋转0-180
{
qreal k = (ptA.x()-ptB.x())/(ptB.y()-ptA.y());//中垂线斜率
qreal b = (ptA.y()+ptB.y())/2-k
(ptA.x()+ptB.x())/2;
//求AB线中垂线上离AB中点20个像素的点C的坐标
x = 20cos(atan(k))+ptCenter.x();
y = k
x+b;
}
else if(ptB.y()<ptA.y())//顺时针旋转180-360
{
qreal k = (ptA.x()-ptB.x())/(ptB.y()-ptA.y());//中垂线斜率
qreal b = (ptA.y()+ptB.y())/2-k*(ptA.x()+ptB.x())/2;
//求AB线中垂线上离AB中点20个像素的点C的坐标
x = -20cos(atan(k))+ptCenter.x();
y = k
x+b;
}
return QPointF(x,y);
}

  • 1
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值