往期回顾
Qt绘图与图形视图之自定义图元实现拖拽、拉伸、旋转功能
一、最终效果
实现对自定义图元的旋转,拖拽、拉伸功能
二、主要思路
主要是三个需要处理的点,1、自己创建一个自定义图元,2、图元的旋转,拖拽、拉伸功能逻辑实现,3、鼠标样式改变
1、主类的初始化
最开始在主类里进行的初始化
这个很简单,没什么好说的,记得顺序,是先视图,再是创建场景,创建图元并添加
ch88_CustomRectItem::ch88_CustomRectItem(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
QRect rect = ui.graphicsView->rect();
// 创建场景
QGraphicsScene* pScene = new QGraphicsScene(rect);
// 视图里设置场景
ui.graphicsView->setScene(pScene);
// 创建自定义图元
MyRectItem* pRectItem = new MyRectItem();
// 场景添加图元
pScene->addItem(pRectItem);
}
三、如何自定义图元
1、设计思路
首先创建一个类继承自QGraphicsItem的,重写其父类的虚函数,重绘图案,本身调用函数可以重绘的图案有很多种的,重写鼠标事件,最后处理旋转的逻辑实现
2、新建C++类派生于QGraphicsItem
注意新建一个C++类,是需要直接派生于QGraphicsItem
class MyRectItem : public QObject, public QGraphicsItem
{
Q_OBJECT
public:
MyRectItem(QGraphicsItem* parent = nullptr);
~MyRectItem();
// 必须重写该函数
QRectF boundingRect() const override;
这里为什么必须重写该函数?
因为它本身是QGraphicsItem类的一个虚函数,如果不重写,那么MyRectItem类将也是一个虚函数类,无法创建对象的
virtual QRectF boundingRect() const = 0;
这个函数的作用是返回该图形项的边界矩形,即包围该图形项的最小矩形区域
QRectF MyRectItem::boundingRect() const
{
QRectF boundingRectF = m_oldRectPolygon.boundingRect();
return QRectF(boundingRectF.x() - 40, boundingRectF.y() - 40,
boundingRectF.width() + 80, boundingRectF.height() + 80);
}
这个boundingRect()函数会返回一个比实际边界矩形更大一些的矩形区域,这样可以确保在绘制和碰撞检测时有足够的空间。
3、构造函数初始化
构造函数里先初始化各项值,设置矩形,此外还要设置矩形的大小和位置
MyRectItem::MyRectItem(QGraphicsItem* parent) :
m_bResize(false),
m_oldRect(0, 0, 200, 200),
m_bRotate(false),
m_RotateAngle(0),
m_StateFlag(DEFAULT_FLAG)
{
setRectSize(m_oldRect);
//设置光标形状,手的形状
setCursor(Qt::ArrowCursor);
//设置图元是可移动的
setFlags(QGraphicsItem::ItemIsMovable
| QGraphicsItem::ItemIsSelectable
| QGraphicsItem::ItemIsFocusable);
m_pPointFofSmallRotateRect = new QPointF[4];
SetRotate(0);
}
4、重写鼠标事件函数
另外还需要重写一下这四个鼠标事件函数
注意参数类型,不再是QMouseEvent了,而是QGraphicsSceneMouseEvent
void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget) override;
void mousePressEvent(QGraphicsSceneMouseEvent* event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override;
后面的几个思路:
4.1、绘制旋转后的矩形和圆形
4.2、 鼠标样式改变
主要是在鼠标按下事件里处理,毕竟确实是要鼠标按下了,样式才会改变,逻辑判断还是比较简单的,if-else语句先判断鼠标的位置,然后根据位置设置样式即可
4.3、鼠标移动的状态判断
鼠标移动这里的判断比较复杂,主要是根据m_StateFlag的状态来判断当前是旋转、移动矩形还是移动矩形的边线,然后再根据不同的状态进行不同的处理
1、如果是旋转矩形状态,根据鼠标位置计算旋转角度,并调用SetRotate函数设置旋转角度,然后调用setRectSize函数恢复矩形的位置和大小,并更新场景
2、如果是移动矩形状态(MOV_RECT),则根据鼠标移动的距离调用moveBy函数移动矩形,然后调用setRectSize函数恢复矩形的位置和大小,并更新场景
3、如果是移动矩形的边线状态(MOV_LEFT_LINE、MOV_TOP_LINE、MOV_RIGHT_LINE、MOV_BOTTOM_LINE)则根据鼠标位置计算新的矩形边界,调用setRectSize函数设置新的矩形大小,并更新场景。
4.4、鼠标松开后状态判断
主要就是判断是不是在旋转状态,如果是那么需要恢复状态为默认值,只要不是旋转,就不改变状态,所以迭代。
5、最难的旋转功能实现
旋转逻辑实现,拉伸拖拽功能都还好,比较难的是旋转功能的实现,毕竟旋转角度很多,而且旋转后每个点的位置坐标都将改变,需要一一获取
5.1、设置旋转角度
void MyRectItem::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();
}
5.2、获取旋转后的点
QPointF MyRectItem::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(angle * M_PI / 180) - (y - dy) * sin(angle * M_PI / 180) + dx;
yy = (x - dx) * sin(angle * M_PI / 180) + (y - dy) * cos(angle * M_PI / 180) + dy;
return QPointF(xx, yy);
}
5.3、获取旋转后的多个点
QList<QPointF> MyRectItem::getRotatePoints(QPointF ptCenter, QList<QPointF> ptIns, qreal angle)
{
QList<QPointF> lstPt;
for (int i = 0; i < ptIns.count(); i++)
{
lstPt.append(getRotatePoint(ptCenter, ptIns.at(i), angle));
}
return lstPt;
}
5.4、矩形旋转后返回多边形
QPolygonF MyRectItem::getRotatePolygonFromRect(QPointF ptCenter, QRectF rectIn, qreal angle)
{
QVector<QPointF> 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);
}
5.5、实时获取旋转时矩形正上方的标记
以上就是Qt里自定义图元实现拖拽、拉伸、旋转功能的简单介绍。
都看到这里了,点个赞再走呗朋友~
加油吧,预祝大家变得更强!