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

闲谈:眨眼间,2024年就过去了一半了,年前定下的计划一个都没完成,也是废了。天天刷龙王CP,倒是看得很开心。乘着有空,把之前学习的内容和示例先总结了。

目录

    • 导读
    • 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_OBJECT
    Q_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.mfbz.cn/a/779411.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FPGA_GTX:简要版

1. GTX介绍 Xilinx FPGA的GT意思是Gigabyte Transceiver。通常称呼为Serdes、高速收发器。GT在xilinx不同系列有着不同的产品&#xff0c;从7系列到UltraScale系列分别有GTP、GTX、GTZ、GTH、GTY和GTM。不同GT整体结构上类似&#xff0c;为了支持越来越高的line rate&#xff…

【HTML】-解决页面内容无法选择、复制问题

目录 1、网页内容无法选中 1.1、问题原因 1.2、解决脚本 1.2.1、开启控制台窗口 1.2.2、执行脚本命令 2、内容复制弹出阻止框 2.2、解决脚本 1、网页内容无法选中 1.1、问题原因 今天在访问某一网站平台&#xff0c;需要将内容进行选择、复制时发现不可使用。 在使用…

单选多选提交问卷,代码示例

&#xff45;&#xff4c;&#xff45;&#xff4d;&#xff45;&#xff4e;&#xff54;中 需要对接口返回的数据进行分析。多选问题使用checkbox&#xff0c;单选题使用radio。 多选时可以绑定&#xff4d;&#xff49;&#xff4e;&#xff0f;&#xff4d;&#xff41;&am…

最新扣子(Coze)实战案例:使用图像流做超分,模糊图片秒变清晰,完全免费教程

&#x1f9d9;‍♂️ 大家好&#xff0c;我是斜杠君&#xff0c;手把手教你搭建扣子AI应用。 &#x1f4dc; 本教程是《AI应用开发系列教程之扣子(Coze)实战教程》&#xff0c;完全免费学习。 &#x1f440; 关注斜杠君&#xff0c;可获取完整版教程。&#x1f44d;&#x1f3f…

[笔记] 卷积 - 02 滤波器在时域的等效形式

1.讨论 这里主要对时域和频域的卷积运算的特征做了讨论&#xff0c;特别是狄拉克函数的物理意义。 关于狄拉克函数&#xff0c;参考这个帖子&#xff1a;https://zhuanlan.zhihu.com/p/345809392 1.狄拉克函数提到的好函数的基本特征是能够快速衰减&#xff0c;对吧&#xf…

Sharding-JDBC

一、概念&#xff1a; Sharding-JDBC是一个在客户端的分库分表工具。它是一个轻量级Java框架&#xff0c;在Java的JDBC层提供的额外服务。 ShardingSphere提供标准化的数据分片、分布式事务和数据治理功能。 二、架构图&#xff1a; ShardingRuleConfiguration 可以包含多个 T…

WEB编程-了解Tomcat服务器

第⼀章⽹络编程 1.1 概述 计算机⽹络&#xff1a;是指将地理位置不同的具有独⽴功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在⽹络 操作系统、⽹络管理软件及⽹络通信协议的管理和协调下&#xff0c;实现资源共享和信息传递的计算机系统。 …

Python 获取tiktok视频评论回复数据 api接口

TIKTOK api接口 用于爬取tiktok视频评论回复数据 详细采集页面如图 https://www.tiktok.com/dailymail/video/7329872821990182190?qneural%20link&t1706783508149 请求API http://api.xxxx.com/tt/video/info/comment/reply?video_id7288909913185701125&comment_…

uni-app使用ucharts地图,自定义Tooltip鼠标悬浮显示内容并且根据@getIndex点击事件获取点击的地区下标和地区名

项目场景&#xff1a; uni-app使用ucharts地图,自定义Tooltip鼠标悬浮显示内容并且根据getIndex点击事件获取点击的地区下标和地区名 例如&#xff1a; 问题描述 官方给的文档有限&#xff0c;需要自己下载地图json数据然后自己渲染和编写鼠标悬浮显示内容以及获取点击地址…

Numpy矩阵运算

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Numpy概述 Numpy是Python的一个开源数值计算扩展库&#xff0c;主要用于存储和处理大型多维数组和矩阵&#xff0c;并且提供了大量的数学函数来操作这些数组。Numpy是Pytho…

为适配kubelet:v0.4 安装指定版本的docker

系统版本信息 cat /etc/redhat-release CentOS Linux release 7.6.1810 (Core) iso 文件下载地址 https://vault.centos.org/7.6.1810/isos/x86_64/CentOS-7-x86_64-DVD-1810.iso0.4 版本的kubelet 报错信息记录 E0603 19:00:38.273720 44142 kubelet.go:734] Error synci…

昇思25天学习打卡营第1天|初识MindSpore

# 打卡 day1 目录 # 打卡 day1 初识MindSpore 昇思 MindSpore 是什么&#xff1f; 昇思 MindSpore 优势|特点 昇思 MindSpore 不足 官方生态学习地址 初识MindSpore 昇思 MindSpore 是什么&#xff1f; 昇思MindSpore 是全场景深度学习架构&#xff0c;为开发者提供了全…

昇思25天学习打卡营第5天|网络与模型相关要素探讨

目录 从 MindSpore 模块中导入nn和ops 定义模型类 模型层 nn.Flatten nn.Dense nn.ReLU nn.SequentialCell nn.Softmax 模型参数 从 MindSpore 模块中导入nn和ops 将 MindSpore 整个模块引入到当前的 Python 脚本里&#xff0c;方便后续运用 MindSpore 所提供的各类功能…

【ue5】虚幻5同时开多个项目

正常开ue5项目我是直接在桌面点击快捷方式进入 只会打开一个项目 如果再想打开一个项目需要进入epic 再点击启动就可以再开一个项目了

预测未来 | Matlab实现HMM隐马尔科夫时间序列预测未来

预测未来 | Matlab实现HMM隐马尔科夫时间序列预测未来 目录 预测未来 | Matlab实现HMM隐马尔科夫时间序列预测未来效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.预测未来 | Matlab实现HMM隐马尔科夫时间序列预测未来 2.运行环境为Matlab2023b及以上&#xff1b; 3…

地级市数字经济指数、互联网用户数、数字金融普惠指数

2000-2022年地级市数字经济指数&#xff08;含控制变量&#xff09; 目录 数字经济如何改善环境污染 一、引言 二、文献综述 三、实证模型 四、数据来源 五、程序代码 六、运行结果 数字经济如何改善环境污染 摘要&#xff1a; 本论文旨在探讨数字经济对环境污染的改善作…

软考中级数据库系统工程师备考经验分享

前几天软考成绩出了&#xff0c;赶紧查询了一下发现自己顺利通过啦&#xff08;上午63&#xff0c;下午67&#xff0c;开心&#xff09;&#xff0c;因此本文记录一下我的备考经验分享给大家。因为工作中项目管理类的知识没有系统学习过&#xff0c;本来想直接报名软考高级证书…

Thisjavabean对象数组

This 1.概念 this是一个对象this是一个构造函数 2.介绍 解决局部变量和成员变量命名冲突 this在面向对象-封装那一篇里&#xff0c;有被两个地方提及。 但我们先简单给一个例子&#xff1a; public Person(String name, String phone, String qqPassword, String bankCar…

踩坑:Unity导出WebGL发布到手机上竖屏时强制显示横屏

具体的适配问题 公司的项目需要将游戏导出WebGL 发布到Web平台 本以为是个很简单的事情 谁知道却被个横竖屏适配搞的头晕 毕竟只有大学浅浅的学了下HTML这门语言 出来工作后基本上都是在跟C# Lua打交道 言归正传 看看具体问题吧 游戏如果从横屏进入 基本上不会有什么适配问题…

Rabnud博士加入了一个社交圈。起初他有5个朋友。他注意到他的朋友数量以下面的方式增长。第1周少了1个朋友......

Rabnud博士加入了一个社交圈。起初他有5个朋友。他注意到他的朋友数量以下面的 方式增长。第1周少了1个朋友&#xff0c;剩下的朋友数量翻倍&#xff1b;第2周少了2个朋友&#xff0c;剩下的朋友数量 翻倍。一般而言&#xff0c;第N周少了N个朋友&#xff0c;剩下的朋友数量翻倍…