Qt 场景(QGraphicsScene)自绘可自由变换与移动的图元(QGraphicsObject)

文章目录

      • 效果图
      • 矩形四个角
      • 矩形

效果图

在这里插入图片描述

  • scene上绘制一个图元QGraphicsObject的矩形,可以自由拖动且拖动四个角可以自由变换矩形需要如下处理。

矩形四个角

  1. 四个角的点需要独立处理继承于QGraphicsObject,当我们点击时拖动时发送信号给矩形,进行矩形变换。
  • 变量
public:/// @brief 点位int pointIndex;/// @brief 偏移位QPointF pressPoint;
RectanglePoint::RectanglePoint(ICanvasScene* canvasScene, int index, QGraphicsItem* parent): QGraphicsObject(parent), pointIndex(index)
{setAcceptHoverEvents(true);
}RectanglePoint::~RectanglePoint() { }QList<RectanglePoint*> RectanglePoint::createRectanglePoints(ICanvasScene* canvasScene, QPolygonF& cachePoints)
{QList<RectanglePoint*> result;if (nullptr == canvasScene) {return result;}for (int i = 0; i < cachePoints.size(); ++i) {RectanglePoint* point = new RectanglePoint(canvasScene, i);if (point) {result.append(point);}}return result;
}QRectF RectanglePoint::boundingRect() const
{return QRectF(-controlSize, -controlSize, controlSize * 2, controlSize * 2);
}void RectanglePoint::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { }void RectanglePoint::mousePressEvent(QGraphicsSceneMouseEvent* event)
{if (event->button() != Qt::LeftButton) {return GraphicsLayer::mousePressEvent(event);}event->accept();pressPoint = event->scenePos();emit positionChanged(QPointF(), pointIndex, startedChanged);
}void RectanglePoint::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{if (!event->buttons().testFlag(Qt::LeftButton)) {return GraphicsLayer::mouseMoveEvent(event);}QPointF startP = pressPoint;QPointF offset = event->scenePos() - startP;event->accept();emit positionChanged(offset, pointIndex, inChanged);
}void RectanglePoint::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{if (event->button() != Qt::LeftButton) {return GraphicsLayer::mouseReleaseEvent(event);}QPointF startP = pressPoint;QPointF offset = event->scenePos() - startP;event->accept();emit positionChanged(offset, pointIndex, finishedChanged);
}
  • createRectanglePoints函数是静态函数,矩形生成时会调用,并传入四个点位。
  • positionChanged的第三个参数为当前操作的状态的枚举(鼠标点击,移动与释放)

矩形

  • 变量
private:/// @brief 当前矩形大小QRectF mboundingRect;/// @brief 矩形的四个点QPolygonF cachePoints;/// @brief 变换的点位QPolygonF startChangePoints;/// @brief 四个角的类QList<RectanglePoint*> rectanglePoints;
const qreal controlSize = 5.0;
GisGridCustomRectItem::GisGridCustomRectItem(QRectF boundingRect, ICanvasScene* canvasScene, QGraphicsItem* parent): QGraphicsObject(parent),mboundingRect(boundingRect)
{setFlags(QGraphicsObject::ItemIsMovable | QGraphicsObject::ItemIsSelectable| QGraphicsObject::ItemSendsGeometryChanges); // 允许移动cachePoints << boundingRect.topLeft() << boundingRect.topRight() << boundingRect.bottomRight()<< boundingRect.bottomLeft();rectanglePoints = RectanglePoint::createRectanglePoints(canvasScene, cachePoints);QListIterator<RectanglePoint*> RectanglePointIter(rectanglePoints);while (RectanglePointIter.hasNext()) {RectanglePoint* RectanglePointGraph = RectanglePointIter.next();if (RectanglePointGraph) {RectanglePointGraph->setParentItem(this);connect(RectanglePointGraph, &RectanglePoint::positionChanged, this,&GisGridCustomRectItem::onRectPositionChange);RectanglePointGraph->setZValue(RectanglePointGraph->zValue() + 1);}}refreshPointPoistion();
}GisGridCustomRectItem::~GisGridCustomRectItem() { }void GisGridCustomRectItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
{painter->save();QPen pen;painter->setBrush(QColor(230, 230, 230, 100));if (isSelected()) {pen.setColor(QColor(0x2E9FE6));}painter->setPen(pen);QPainterPath path;path.addPolygon(cachePoints);path.closeSubpath();painter->drawPath(path);if (isSelected()) {drawPointsRect(painter);}painter->restore();
}QRectF GisGridCustomRectItem::boundingRect() const
{return mboundingRect;return shape().controlPointRect();
}QPainterPath GisGridCustomRectItem::shape() const
{return QGraphicsObject::shape();QPainterPath path;path.addPolygon(cachePoints);path.closeSubpath();QPainterPathStroker stroker;stroker.setWidth(15);path = stroker.createStroke(path);return path;
}QVariant GisGridCustomRectItem::itemChange(GraphicsItemChange change, const QVariant& value)
{// if (change == QGraphicsItem::ItemPositionChange && scene()) {//     return Utility::pointAlignmentToGrid(value.toPointF(), 10);// }return QGraphicsObject::itemChange(change, value);
}void GisGridCustomRectItem::drawPointsRect(QPainter* painter)
{double arrowLength = 7; // 箭头长度// 四个角落上的箭头,一个角绘制两条箭头构成一个双向箭头// 计算45度角(PI/4 弧度)和225度角(5*PI/4 弧度)double angle45 = M_PI / 4;double angle225 = 5 * M_PI / 4;// 左上角的双向箭头drawSingleArrow(painter, cachePoints[0], angle45, arrowLength);  // 对角向右下drawSingleArrow(painter, cachePoints[0], angle225, arrowLength); // 对角向左上// 右上角的双向箭头drawSingleArrow(painter, cachePoints[3], -angle45, arrowLength);       // 对角向左下drawSingleArrow(painter, cachePoints[3], M_PI - angle45, arrowLength); // 对角向右上// 左下角的双向箭头drawSingleArrow(painter, cachePoints[1], M_PI - angle45, arrowLength); // 对角向左下drawSingleArrow(painter, cachePoints[1], -angle45, arrowLength);       // 对角向右上// 右下角的双向箭头drawSingleArrow(painter, cachePoints[2], angle225 - M_PI, arrowLength); // 对角向右上drawSingleArrow(painter, cachePoints[2], angle225, arrowLength);        // 对角向左下
}void GisGridCustomRectItem::onRectPositionChange(QPointF delta, int index, itemRectChange state)
{ICanvasScene* canvasScene = getCanvasScene();if (!canvasScene) {return;}ICanvasView* canvasView = canvasScene->getCanvasView();if (!canvasView) {return;}auto interactioMode = (InteractionMode)canvasView->getInteractionMode();if (interactioMode == kAreaAmplification) {return;}switch (state) {case startedChanged:startChangePoints = cachePoints;break;case inChanged:changeCachePoints(delta, index);break;case finishedChanged:changeCachePoints(delta, index);refreshPointPoistion();default:break;}update();
}void GisGridCustomRectItem::changeCachePoints(QPointF delta, int index)
{QPolygonF pointF;cachePoints[index] = startChangePoints[index] + delta;if (index == 1 || index == 3) {pointF << cachePoints[1] << cachePoints[3];} else {pointF << cachePoints[0] << cachePoints[2];}QPointF p1 = pointF[0];QPointF p2 = pointF[1];QPointF p3(p2.x(), p1.y());QPointF p4(p1.x(), p2.y());pointF.clear();if (index == 1 || index == 3) {pointF << p3 << p1 << p4 << p2;} else {pointF << p1 << p3 << p2 << p4;}cachePoints = pointF;// 更新矩形大小double minX = std::min({ p1.x(), p2.x(), p3.x(), p4.x() });double minY = std::min({ p1.y(), p2.y(), p3.y(), p4.y() });double maxX = std::max({ p1.x(), p2.x(), p3.x(), p4.x() });double maxY = std::max({ p1.y(), p2.y(), p3.y(), p4.y() });QRectF rect(minX, minY, maxX - minX, maxY - minY);mboundingRect = rect;
}void GisGridCustomRectItem::drawSingleArrow(QPainter* painter, QPointF basePoint, double angle, double length)
{// 计算箭头的末端点QPointF endPoint(basePoint.x() + length * qCos(angle), basePoint.y() + length * qSin(angle));// 绘制箭头的主线painter->drawLine(basePoint, endPoint);// 绘制箭头的两个小边,构成箭头尖端double arrowHeadAngle = M_PI / 5; // 箭头头部的夹角double arrowHeadLength = 5;       // 箭头头部长度QPointF arrowLeft(endPoint.x() - arrowHeadLength * qCos(angle - arrowHeadAngle),endPoint.y() - arrowHeadLength * qSin(angle - arrowHeadAngle));QPointF arrowRight(endPoint.x() - arrowHeadLength * qCos(angle + arrowHeadAngle),endPoint.y() - arrowHeadLength * qSin(angle + arrowHeadAngle));painter->drawLine(endPoint, arrowLeft);painter->drawLine(endPoint, arrowRight);
}void GisGridCustomRectItem::refreshPointPoistion()
{QListIterator<RectanglePoint*> rectanglePointsIter(rectanglePoints);while (rectanglePointsIter.hasNext()) {RectanglePoint* ponit = rectanglePointsIter.next();if (ponit) {ponit->setPos(cachePoints[ponit->pointIndex]);}}
}
  • 四个角的双向箭头挺难画的,可以直接画一个简单的圆形就行。
  • 对于移动操作而言,应该设置ItemSendsGeometryChanges标志,这允许项目在几何变化时(如位置改变)发送通知。如果没有设置这个标志,项目在移动时将不会调用itemChange函数。
  • 拖动矩形有俩种处理形式(看shapeboundingRect函数)
    1. 点击边框拖动,这个需要计算shape路径函数,得到矩形的边框。
    2. 点击矩形内部拖动,这个需要再每次变换后更新一下矩形。
  • 矩形的变换关键在于changeCachePoints函数中的处理,其实一个矩形只需要俩个点,另外俩个点根据这俩个点变换,不然你拖动起来可能不是一个矩形了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/485011.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

猫头虎分享已解决Bug || Web服务故障:WebServiceUnavailable, HTTPServerError

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

记录解决uniapp使用uview-plus在vue3+vite+ts项目中打包后样式不能显示问题

一、背景 从 vue2uview1 升级到 vue3vitetsuview-plus ,uview组件样式打包后不显示&#xff0c;升级前uview 组件是可以正常显示&#xff0c;升级后本地运行是可以正常显示&#xff0c;但是打包发布成H5后uview的组件无法正常显示&#xff0c;其他uniapp自己的组件可以正常显示…

PacketSender-用于发送/接收 TCP、UDP、SSL、HTTP 的网络实用程序

PacketSender-用于发送/接收 TCP、UDP、SSL、HTTP 的网络实用程序 PacketSender是一款开源的用于发送/接收 TCP、UDP、SSL、HTTP 的网络实用程序&#xff0c;作者为dannagle。 其官网地址为&#xff1a;https://packetsender.com/&#xff0c;Github源代码地址&#xff1a;htt…

Kubernetes kubeadm 证书到期,更新证书

1.环境说明 lient Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.6", GitCommit:"fbf646b339dc52336b55d8ec85c181981b86331a", GitTreeState:"clean", BuildDate:"2020-12-18T12:09:30Z", G…

糖尿病性视网膜病变(DR)的自动化检测和分期

糖尿病性视网膜病变&#xff08;DR&#xff09;的自动化检测和分期 提出背景DR的阶段及其特征 历年解法计算机视觉方法多分类方法 新的解法深度学习方法迁移学习大模型多模型集成全流程分析 总结特征1&#xff1a;图像分割特征2&#xff1a;疾病分级特征3&#xff1a;治疗建议生…

phaseDNN文章解读

文章DOI: https://doi.org/10.48550/arXiv.1905.01389 作者是 Southern Methodist University 的Wei Cai 教授 A Parallel Phase Shift Deep Neural Network for Adaptive Wideband Learning 一种并行移相深度神经网络来自适应学习宽带频率信号 20190514 核心思想&#xff1a;…

搞明白Eclipse的Java Build Path很重要

为什么说搞懂Eclipse的Java Build Path很重要呢&#xff1f;因为搞懂了它才知道eclipse如何放置源代码和输出文件的&#xff0c;也就是搞懂从哪里来到哪里去的问题。 Build Path是指定Java工程所包含的资源属性集合。 在一个成熟的Java工程中&#xff0c;不仅仅有自己编写的源…

数字之美:探索人工智能绘画的奇妙世界

目录 引言AI绘画的定义与发展历程定义与发展历程AI绘画产品有哪些? AI绘画的应用领域设计与创意产业影视与游戏制作数字艺术与展览 AI绘画的基本原理与技术深度学习与神经网络生成对抗网络&#xff08;GAN&#xff09;风格迁移算法 AI绘画效果展示一只带着墨镜的小猫在高楼林立…

openGauss学习笔记-226 openGauss性能调优-系统调优-配置LLVM-LLVM适用场景与限制

文章目录 openGauss学习笔记-226 openGauss性能调优-系统调优-配置LLVM-LLVM适用场景与限制226.1 适用场景226.2 非适用场景 openGauss学习笔记-226 openGauss性能调优-系统调优-配置LLVM-LLVM适用场景与限制 226.1 适用场景 支持LLVM的表达式 查询语句中存在以下的表达式支持…

☀️将大华摄像头画面接入Unity 【2】配置Unity接监控画面

一、前言 上一篇咱们将大华摄像头接入到电脑上了&#xff0c;接下来准备接入到unity画面。 接入到监控就涉及到各种视频流的格式rtsp、rtmp、m3u8。 Unity里有一些播放视频流的插件&#xff0c;主要的就是AVPro Video 和 UMP等&#xff0c;这次我用的是UMP 最好使用2.0.3版本…

HAL STM32 HW I2C DMA + SSD1306/SH1106驱动示例

HAL STM32 HW I2C DMA SSD1306/SH1106驱动示例 &#x1f4cd;硬件I2C DMA驱动参考&#xff1a;https://blog.csdn.net/weixin_45065888/article/details/118225993 &#x1f4cc;github上的相关项目&#xff1a;https://github.com/taburyak/STM32_OLED_SSD1306_HAL_DMA &am…

使用单一ASM-HEMT模型实现从X波段到Ka波段精确的GaN HEMT非线性仿真

来源&#xff1a;Accurate Nonlinear GaN HEMT Simulations from X- to Ka-Band using a Single ASM-HEMT Model 摘要&#xff1a;本文首次研究了ASM-HEMT模型在宽频带范围内的大信号准确性。在10、20和30 GHz的频率下&#xff0c;通过测量和模拟功率扫描进行了比较。在相同的频…