QT使用QGraphicsView绘图 重写QGraphicsObject类实现点在QPainterPath路径上移动动画效果

闲谈:眨眼间,2024年就过去了一半了,年前定下的计划一个都没完成,乘着有空,把之前学习的内容和示例先总结了。

目录

    • 导读
    • SVG 转QPainterPath 路径
    • 获取QPainterPath指定长度时的坐标。
    • 重写QGraphicsObject类 实现点图元
    • QPropertyAnimation 动画类的使用
    • 功能实现:
      • 示例图:
      • 完整源码:

导读

看wps的流程图,发现有一个连接线获取焦点会显示多个点按照箭头方向移动的功能效果,如:
请添加图片描述
我就在想这种效果如何通过QGraphicsSvgItem图元实现的:
一开始我是打算直接加载SVG文件,SVG文件中直接使用animateMotion属性就能实现这种效果,再通过QGraphicsSvgItem图元的setElementId属性实现动画的显示,
但是Qt的SVG处理模块是不支持animateMotion属性。不会运行动画。
Svg文件效果如:
请添加图片描述
Svg实际文件:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<svg width="600" height="600" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="svg_0" viewBox="0 0 600 600" version="1.1"> <path fill="#FFFFFF" fill-opacity="0" stroke="#000000" stroke-opacity="1" stroke-width="1" d="M268.00 265.00 L264.00 423.00 L418.00 412.00 L462.00 216.00 C462.00 216.00 330.00 159.00 329.00 159.00 C328.00 159.00 119.00 180.00 119.00 180.00 C119.00 180.00 13.00 365.00 13.00 365.00 C13.00 365.00 86.00 537.00 87.00 537.00 C88.00 537.00 341.00 547.00 343.00 547.00 C345.00 547.00 512.00 546.00 512.00 546.00 C512.00 546.00 564.00 326.00 564.00 326.00 C564.00 326.00 547.00 136.00 547.00 136.00 C547.00 136.00 251.00 52.00 251.00 52.00 C251.00 52.00 251.00 52.00 251.00 52.00" id="motionPath" /><circle r="5" fill="red"><animateMotion dur="15s" repeatCount="indefinite"><mpath href="#motionPath"/></animateMotion></circle>
</svg>

Qt中SVG动画效果无效,但是如果获取SVG文件在QGraphicsSvgItem图元绘制的轨迹,在添加一个点按照轨迹移动的动画效果,就能实现这个功能。
但是查看QGraphicsSvgItem图元源码发现,QGraphicsSvgItem图元都是通过QPainter直接绘制的,获取不到QPainterPath 轨迹,

SVG 转QPainterPath 路径

这涉及到SVG文件的解析,看了看QSvgRenderer实现的源码,有点打老壳;
最后通过Github找到一个Qt开发的基于C,C++的SVG编译器源码:
QtSVGEditor
https://github.com/SVGEditors/QtSVGEditor
这其中就是将SVG文件转成的QPainterPath类型绘制:
其他的SVG属性先不管,移植源码中的SVGElementPath.hSVGPathSeg.h文件实现对SVG的path 类型的D属性内容解析,获取到QPainterPath路径轨迹。

CSVGElementPath element;element.parseD(L"M268.00 265.00 L264.00 423.00 L418.00 412.00 L462.00 216.00 C462.00 216.00 330.00 159.00 329.00 159.00 C328.00 159.00 119.00 180.00 119.00 180.00 C119.00 180.00 13.00 365.00 13.00 365.00 C13.00 365.00 86.00 537.00 87.00 537.00 C88.00 537.00 341.00 547.00 343.00 547.00 C345.00 547.00 512.00 546.00 512.00 546.00 C512.00 546.00 564.00 326.00 564.00 326.00 C564.00 326.00 547.00 136.00 547.00 136.00 C547.00 136.00 251.00 52.00 251.00 52.00 C251.00 52.00 251.00 52.00 251.00 52.00");
QPainterPath m_Path=element.resetPath();

获取QPainterPath指定长度时的坐标。

之所以要转换成QPainterPath类,
就是为了能实时获取指定长度时对应在轨迹上的坐标,通过计算指定长度与实际长度的百分比,通过pointAtPercent方法获取到实际坐标;

QPointF QPainterPath::pointAtPercent(qreal t) const
/*
Returns the point at at the percentage t of the current path. The argument t has to be between 0 and 1.
Note that similarly to other percent methods, the percentage measurement is not linear with regards to the length, if curves are present in the path. When curves are present the percentage argument is mapped to the t parameter of the Bezier equations.
返回当前路径的百分比t处的点。参数t必须在0到1之间。
请注意,与其他百分比方法类似,
如果路径中存在曲线,则百分比测量与长度无关。
当曲线存在时,百分比参数被映射到Bezier方程的t参数。
*.

重写QGraphicsObject类 实现点图元

重写QGraphicsObject类 的
QPainterPath shape() const override
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
方法,实现点图元;

QPainterPath TGraphicsPointItem::shape() const
{QPainterPath path;path.addRect(QRectF(-2,-2,selfRect.width(),selfRect.height()));return path;
}void TGraphicsPointItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{QPen pen3(QColor("#DB5E5E"), 1);painter->setPen(pen3);painter->setBrush(QColor("#DB5E5E"));painter->drawEllipse(QRectF(-2,-2,selfRect.width(),selfRect.height()));
}

QPropertyAnimation 动画类的使用

参考:
QT中的动画类(QPropertyAnimation)
QT之QPropertyAnimation详细介绍
使用QPropertyAnimation 类,实时获取QPainterPath路径中对应的点坐标位置,实时设置点坐标 (setPos(坐标))
初始化示例:

  //! 运动轨迹点 长度qreal path_advance=0;//! 点坐标pos=m_Path.pointAtPercent(0);setPos(pos); //! 周期时间,点从0到总长需要的时间qreal goThroughTime=15000;//! path_advance 元属性QPropertyAnimation* animation=new QPropertyAnimation(this,"path_advance");//! 业务值改变时,计算点坐标,移动点connect(animation,&QPropertyAnimation::valueChanged,this,[&](const QVariant &value){//! 计算百分比获取实时坐标pos=m_Path.pointAtPercent((path_advance/m_Path.length()));setPos(pos);});animation->setStartValue(0);//! 设置QPainterPath总长度animation->setEndValue(m_Path.length());//! 动画周期时间animation->setDuration(goThroughTime);//! -1 运行结束后重新开始animation->setLoopCount(-1);animation->start();

总结:
实际上整个动画流程也是读取SVG文件再转换成QPainterPath轨迹,重写QGraphicsObject类生成一个点图元,在添加一个路径元属性结合QPropertyAnimation 动画类实现这种效果;
在找到 QtSVGEditor 库的时候,我发现WPS整个数据库制作流程图模块,完全可以通过对SVG文件的解析和修改来实现。包括其中的各种动画效果也可以通过Qt的动画类,如QPropertyAnimation类等来实现,完成后甚至于能将制作的流程图导出为SVG文件 ,有兴趣的可以去看看。

功能实现:

示例图:

请添加图片描述

完整源码:

TGraphicsPointItem.h:
重写QGraphicsObject
传入SVG文件转成的QPainterPath 轨迹路径,
在通过QPropertyAnimation 控制QGraphicsObject 点图元的坐标移动


#include <QObject>
#include <QGraphicsObject>
#include <QGraphicsScene>#include <QPainterPath>
#include <QPropertyAnimation>
#include <QPainter>#include "tgraphicspointitem.h"class TGraphicsPointItem :public QGraphicsObject
{Q_OBJECTQ_INTERFACES(QGraphicsItem)Q_PROPERTY(qreal path_advance READ get_path_advance WRITE set_path_advance)public:TGraphicsPointItem(QPainterPath path,qreal time=5000,QGraphicsObject *parentItem = nullptr);enum { Type = UserType + 3 };int type() const override{return Type;}QRectF boundingRect() const override;QPainterPath shape() const override;void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;//! 设置轨迹路线 和 走完需要的时间void SetPainterPathByTime(QPainterPath path,qreal time);qreal get_path_advance(){return path_advance;}public slots:void set_path_advance(qreal _path_advance){path_advance=_path_advance;}private:QRectF selfRect=QRectF(0,0,4,4);//! 运动轨迹点 长度qreal path_advance=0;//! 运动轨迹线QPainterPath m_Path;//! 所需时间qreal goThroughTime=0;//! 坐标点QPointF pos;//! 虚线动画QPropertyAnimation* animation;};

TGraphicsPointItem.cpp:

#include "tgraphicspointitem.h"
#include <QDebug>TGraphicsPointItem::TGraphicsPointItem(QPainterPath path,qreal time,QGraphicsObject *parentItem):QGraphicsObject(parentItem)
{this->setAcceptHoverEvents(false);setFlag(QGraphicsItem::ItemIsMovable, false);setFlag(QGraphicsItem::ItemIsSelectable, false);
//    setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
//    setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);SetPainterPathByTime(path, time);setParentItem(parentItem);setZValue(999);show();
}QRectF TGraphicsPointItem::boundingRect() const
{return shape().boundingRect();
}QPainterPath TGraphicsPointItem::shape() const
{QPainterPath path;path.addRect(QRectF(-2,-2,selfRect.width(),selfRect.height()));return path;
}void TGraphicsPointItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{QPen pen3(QColor("#DB5E5E"), 1);painter->setPen(pen3);painter->setBrush(QColor("#DB5E5E"));painter->drawEllipse(QRectF(-2,-2,selfRect.width(),selfRect.height()));}void TGraphicsPointItem::SetPainterPathByTime(QPainterPath path,qreal time)
{m_Path=path;path_advance=0;pos=m_Path.pointAtPercent(0);qDebug()<<" [pos] -->"<<pos;setPos(pos);goThroughTime=time;animation=new QPropertyAnimation(this,"path_advance");connect(animation,&QPropertyAnimation::valueChanged,this,[&](const QVariant &value){
//        qDebug()<<" [valueChanged] -->"<<path_advance;pos=m_Path.pointAtPercent((path_advance/m_Path.length()));setPos(pos);
//        qDebug()<<" [pos] -->"<<pos;
//        this->update();});connect(animation,&QPropertyAnimation::finished,this,[&](){
//        qDebug()<<" [finished] -->";
//        this->parentItem()->scene()->removeItem((QGraphicsItem*)this);});animation->setStartValue(0);animation->setEndValue(m_Path.length());animation->setDuration(goThroughTime);animation->setLoopCount(-1);animation->start();
}

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

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

相关文章

stm32精密控制步进电机(基础篇)

众所周知&#xff0c;步进电机由于使用脉冲控制&#xff0c;会比直流电机的控制稍难一些&#xff0c;但开环控制时也更加稳定。 落到做项目的时候&#xff0c;目前来说我都会先考虑步进电机&#xff0c;再去考虑直流&#xff0c;无刷这样的电机。包括毕设时所用的机械臂也是用…

并发处理 优先图和多重图

优先图(Precedence Graph)视图可串性多重图(Polygraph) 优先图(Precedence Graph) 优先图用于冲突可串性的判断。 优先图结构&#xff1a; 结点 (Node)&#xff1a;事务&#xff1b;有向边 (Arc): Ti → Tj &#xff0c;满足 Ti <s Tj&#xff1b; 存在Ti中的操作A1和Tj…

开源全新H5充值系统源码/自定义首页+充值页面/灵活对接上游渠道接口

开源全新H5充值系统源码&#xff0c;系统基于thinkphp框架开发&#xff0c;功能已全完善&#xff0c;可灵活对接其他上游渠道接口&#xff0c;默认对接了大猿人接口&#xff0c;另外可无限制自定义创建充值页面&#xff0c;首页支持后台自定义修改&#xff0c;支持三级分销&…

史上最全的自抗扰控制(ADRC)学习资料

史上最全的自抗扰控制&#xff08;ADRC&#xff09;学习资料 需要的私信我~ 需要的私信我~ 需要的私信我~ ​ 本文将作者近些年来学习ADRC算法的学习资料进行汇总&#xff0c;整理了这一版相对较全的学习资料&#xff0c;包含参考文献以及仿真案例&#xff0c;适合初学者入门&…

6、Redis系统-数据结构-05-整数

五、整数集合&#xff08;Intset&#xff09; 整数集合是 Redis 中 Set 对象的底层实现之一。当一个 Set 对象只包含整数值元素&#xff0c;并且元素数量不大时&#xff0c;就会使用整数集合这个数据结构作为底层实现。整数集合通过紧凑的内存布局和升级机制&#xff0c;实现了…

HiAI Foundation开发平台,加速端侧AI应用的智能革命

如果您是一名开发者&#xff0c;正在寻找一种高效、灵活且易于使用的端侧AI开发框架&#xff0c;那么HarmonyOS SDKHiAI Foundation服务&#xff08;HiAI Foundation Kit&#xff09;就是您的理想选择。 作为一款AI开发框架&#xff0c;HiAI Foundation不仅提供强大的NPU计算能…

直击2024 WAIC现场:关于大模型,热情、焦虑与冷静同在

世博展览馆内人们的热情&#xff0c;与世博中心内参与论坛的人们&#xff0c;心情似乎并不成正比。 展馆内人们看到的大模型加速落地是表象&#xff0c;也是结果&#xff1b;而论坛里的企业家和人工智能学者们则更关注大模型的未来发展方向和商业化进程&#xff0c;以及AI安全…

计算机的错误计算(二十三)

摘要 计算机的错误计算&#xff08;二十二&#xff09;阐明&#xff1a;对于结果应该为 0的算式&#xff0c;即使增加计算精度&#xff0c;也得不出0. 针对 &#xff0c;本节给出一种解决方案。 计算机的错误计算&#xff08;十九&#xff09;展示了计算机对 的错误计算&…

【0基础学爬虫】爬虫框架之 feapder 的使用

前言 大数据时代&#xff0c;各行各业对数据采集的需求日益增多&#xff0c;网络爬虫的运用也更为广泛&#xff0c;越来越多的人开始学习网络爬虫这项技术&#xff0c;K哥爬虫此前已经推出不少爬虫进阶、逆向相关文章&#xff0c;为实现从易到难全方位覆盖&#xff0c;特设【0…

Python遥感开发之时序数据的线性插值

Python遥感开发之时序数据的线性插值 0 历史博客1 实现思路2 代码实现3 效果展示 前言&#xff1a;在遇到空间数据的时候&#xff0c;尤其是哨兵、Landsat或者MODIS数据会出现局部值的空缺&#xff0c;为了解决这些值的空缺&#xff0c;通常采用插值的方法&#xff0c;本博客使…

Python编程学习笔记(1)--- 变量和简单数据类型

1、变量 在学习编程语言之前&#xff0c;所接触的第一个程序&#xff0c;绝大多数都是&#xff1a; print("Hello world!") 接下来尝试使用一个变量。在代码中的开头添加一行代码&#xff0c;并对第二行代码进行修改&#xff0c;如下&#xff1a; message "…

中国星坤X1224系列线对板连接器:小巧稳定,助力物联网终端高效运行

在物联网、电器和消防等领域&#xff0c;终端设备的安全稳定运行至关重要。为了满足这些领域对连接器高可靠性、小巧轻便和耐高温的需求&#xff0c;X1224系列线对板连接器应运而生。这款连接器以其独特的设计和卓越的性能&#xff0c;成为了终端设备中不可或缺的一部分。 一、…

Ubantu22.04 通过FlatPak安装微信

Ubuntu22.04 下使用Flatpak稳定安装微信&#xff01; 国际惯例&#xff0c;废话不多说&#xff0c;先上效果图。为啥使用Flatpak,因为Wechat官方只在FlatPak发布了最新的版本。之前使用了Wine以及Dock安装Wechat,效果都不是很理想&#xff0c;bug很多。所以使用了FlatPak。 Fl…

免费的鼠标连点器电脑版教程!官方正版!专业鼠标连点器用户分享教程!2024最新

电脑技术的不断发展&#xff0c;许多用户在日常工作和娱乐中&#xff0c;需要用到各种辅助工具来提升效率或简化操作&#xff0c;而电脑办公中&#xff0c;鼠标连点器作为一种能够模拟鼠标点击的软件&#xff0c;受到了广大用户的青睐。本文将为大家介绍一款官方正版的免费鼠标…

一.2.(3)放大电路的图解分析方法和微变等效电路分析方法;

放大电路的主要分析方法:图解法、微变等效电路法 这里以共射放大电路为例 (1) 图解法: 1.静态分析 首先确定静态工作点Q,然后根据电路的特点,做出直流负载线,进而画出交流负载线,最后,画出各极电流电压的波形。求出最大不失真输出电压。 估算IBQ&#xff0c;然后根据数据手册里…

09.QT控件:按钮类控件

1. QPushButton 在QT中使⽤ QPushButton 表⽰⼀个按钮。QPushButton 继承⾃ QAbstractButton&#xff0c;这个类是⼀个抽象类&#xff0c;是其他按钮的⽗类。 &#xff08;1&#xff09;QAbstractButton 中&#xff0c;和 QPushButton 相关性较⼤的属性&#xff1a; &#xf…

STM32-Unix时间戳和BKP备份寄存器以及RTC实时时钟

本内容基于江协科技STM32视频学习之后整理而得。 文章目录 1. Unix时间戳1.1 Unix时间戳简介1.2 UTC/GMT1.3 时间戳转换 2. BKP备份寄存器2.1 BKP简介2.2 BKP基本结构2.3 BKP库函数 3. RTC实时时钟3.1 RTC简介3.2 RTC框图3.3 RTC基本结构3.4 硬件电路3.5 RTC操作注意事项3.6 R…

vue事件处理v-on或@

事件处理v-on或 我们可以使用v-on指令&#xff08;简写&#xff09;来监听DOM事件&#xff0c;并在事件触发时执行对应的Javascript。用法&#xff1a;v-on:click"methodName"或click"hander" 事件处理器的值可以是&#xff1a; 内敛事件处理器&#xff1…

Golang | Leetcode Golang题解之第213题打家劫舍II

题目&#xff1a; 题解&#xff1a; func _rob(nums []int) int {first, second : nums[0], max(nums[0], nums[1])for _, v : range nums[2:] {first, second second, max(firstv, second)}return second }func rob(nums []int) int {n : len(nums)if n 1 {return nums[0]}…

SSRF靶场通关合集

目录 前言 SSRF总结 1.pikachu 1.1SSRF(curl) 1.1.1http协议 1.1.2 file协议查看本地文件 1.1.3 dict协议扫描内网主机开放端口 1.2 SSRF&#xff08;file_get_content&#xff09; 1.2.1 file读取本地文件 1.2.2 php://filter/读php源代码 2.DoraBox靶场 前言 最近…