QT实现QGraphicsView绘图 重写QGraphicsSvgItem类实现边框动画

目录导读

    • 简述
      • 使用 QTimer 实现 QGraphicsSvgItem 边框动画效果

简述

在了解学习WPS的流程图的时候,发现它这个选择图元有个动态边框效果,而且连接线还会根据线生成点从头移动到尾的动画。像这种:
请添加图片描述
在QML中实现这种动画属性很简单,现成的动画属性,但是在QGraphicsView中实现这种效果就值得思考一下,
在QT中SVG的动画属性只支持animateTransform元素,其他动画元素是不支持。即使我把QT版本升级成 6.7.0版本 也不支持,
例如 animate 动画元素:
请添加图片描述
SVG文件:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"><svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink/" baseProfile="tiny" version="1.2"><title>Spheres</title><path id="dashedSquare" d="M10,10 L190,10 L190,190 L10,190 Z M10,10" stroke="black" stroke-width="2" fill="none" stroke-dasharray="10,5" stroke-dashoffset="0" stroke-linecap="round"><animate  id="dashAnimate" attributeName="stroke-dashoffset" from="200" to="0" dur="2s" begin="0s"  repeatCount="indefinite" /><animate  id="resetAnimate" attributeName="stroke-dashoffset" from="0" to="200" dur="2s"  begin="dashAnimate.end" fill="freeze" />
</path>
</svg>

这个SVG是实现了边框的动态效果;
但是在QGraphicsViewQGraphicsSvgItem中是不会有动画效果的,包括QSvgWidget等其他Svg相关的控件也没有,只能尝试其他办法。。

  • 使用 QTimer 实现 QGraphicsSvgItem 边框动画效果

使用QT的 QTimer 类 实现 QGraphicsSvgItem 边框动画,
通过研究上面的SVG可以发现,边框的动画效果实际是stroke-dashoffset 属性的变动,也可以通过
QPen 类的 setDashOffset(qreal offset) 变动,
所以在
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
绘制图元时,只要使用 QTimer 周期性将画笔的DashOffset值来回的修改就可以了,

值得注意的是

  1. 通过研究 QGraphicsSvgItem类源码中的 paint 事件发现 QGraphicsItem自带的选中显示边框效果中的边框是外扩了一定像素,
    参考源码:
//! 这是 QGraphicsItem 自带选中边框效果的绘制边框
//! 源码参考路径:  D:\Qt\Qt5.13.1\5.13.1\Src\qtsvg\src\svg\qgraphicssvgitem.cpp
//! \internal
//! Highlights \a item as selected.
//! NOTE: This function is a duplicate of qt_graphicsItem_highlightSelected() in qgraphicsitem.cpp!static void qt_graphicsItem_highlightSelected(QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option)
{const QRectF murect = painter->transform().mapRect(QRectF(0, 0, 1, 1));if (qFuzzyIsNull(qMax(murect.width(), murect.height())))return;const QRectF mbrect = painter->transform().mapRect(item->boundingRect());if (qMin(mbrect.width(), mbrect.height()) < qreal(1.0))return;qreal itemPenWidth;switch (item->type()) {case QGraphicsEllipseItem::Type:itemPenWidth = static_cast<QGraphicsEllipseItem *>(item)->pen().widthF();break;case QGraphicsPathItem::Type:itemPenWidth = static_cast<QGraphicsPathItem *>(item)->pen().widthF();break;case QGraphicsPolygonItem::Type:itemPenWidth = static_cast<QGraphicsPolygonItem *>(item)->pen().widthF();break;case QGraphicsRectItem::Type:itemPenWidth = static_cast<QGraphicsRectItem *>(item)->pen().widthF();break;case QGraphicsSimpleTextItem::Type:itemPenWidth = static_cast<QGraphicsSimpleTextItem *>(item)->pen().widthF();break;case QGraphicsLineItem::Type:itemPenWidth = static_cast<QGraphicsLineItem *>(item)->pen().widthF();break;default:itemPenWidth = 1.0;}const qreal pad = itemPenWidth / 2;const qreal penWidth = 0; // cosmetic penconst QColor fgcolor = option->palette.windowText().color();const QColor bgcolor( // ensure good contrast against fgcolorfgcolor.red()   > 127 ? 0 : 255,fgcolor.green() > 127 ? 0 : 255,fgcolor.blue()  > 127 ? 0 : 255);painter->setPen(QPen(bgcolor, penWidth, Qt::SolidLine));painter->setBrush(Qt::NoBrush);painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad));painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine));painter->setBrush(Qt::NoBrush);painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad));
}

所以在重写
void QGraphicsSvgItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
事件的时候,不能继承原有paint;
只需要以下内容:

void TGraphicsSvgItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{if (!renderer()->isValid())return;if (elementId().isEmpty())renderer()->render(painter, boundingRect());elserenderer()->render(painter, elementId(), boundingRect());if(isSelected()){QPen pen2(QColor("#067BEF"), 2);pen2.setDashPattern(QVector<qreal>{2,2}); // 设置虚线模式pen2.setDashOffset(dashoffset);painter->setPen(pen2);painter->drawRect(shape().boundingRect()); // 绘制正方形}
}

2.不使用QPropertyAnimation类而是用QTimer 类实现DashOffset值周期性变化,
QPropertyAnimation类 变化的差值是线性的,看不出边框虚线效果。
需要设置按指定步长(10)的大小递增递减才能看出完整效果。

timer = new QTimer();QObject::connect(timer, &QTimer::timeout,[&](){dashoffset-=10;if(dashoffset<=0){dashoffset=100;}this->update();});

实际效果:
请添加图片描述
完整代码:

TGraphicsSvgItem.h

#ifndef TGRAPHICSSVGITEM_H
#define TGRAPHICSSVGITEM_H#include <QGraphicsItem>
#include <QGraphicsSvgItem>
#include <QTimer>
#include <QPainter>
#include <QSvgRenderer>#include <QPropertyAnimation>
#include <QParallelAnimationGroup>
#include <QSequentialAnimationGroup>
#include "ParseSvg/lib_csvgelementpath.h"
#include "tgraphicspointitem.h"//! D:\Qt\Qt5.13.1\5.13.1\Src\qtsvg\src\svg\qgraphicssvgitem.cpp
//!
class TGraphicsSvgItem:public QGraphicsSvgItem
{Q_OBJECT
public:TGraphicsSvgItem(QGraphicsItem *parentItem = nullptr);TGraphicsSvgItem(const QString &fileName, QGraphicsItem *parentItem = nullptr);public slots://! 初始化计时器void Init_timer();
protected:
//    QRectF boundingRect() const override;void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
private:qreal dashoffset=100;//! 虚边框线QTimer* timer;
};#endif // TGRAPHICSSVGITEM_H

TGraphicsSvgItem.cpp

#include "tgraphicssvgitem.h"
#include <QDebug>
#include <QPropertyAnimation>
#include <QTimeLine>TGraphicsSvgItem::TGraphicsSvgItem(QGraphicsItem *parentItem):QGraphicsSvgItem(parentItem)
{this->setAcceptHoverEvents(true);Init_timer();
}TGraphicsSvgItem::TGraphicsSvgItem(const QString &fileName, QGraphicsItem *parentItem):QGraphicsSvgItem(fileName,parentItem)
{this->setAcceptHoverEvents(true);Init_timer();}void TGraphicsSvgItem::Init_timer()
{this->setFlag(TGraphicsSvgItem::ItemIsMovable, true);this->setFlag(TGraphicsSvgItem::ItemIsSelectable, true);this->setFlag(TGraphicsSvgItem::ItemClipsToShape,true);timer = new QTimer();QObject::connect(timer, &QTimer::timeout,[&](){dashoffset-=10;if(dashoffset<=0){dashoffset=100;}this->update();});
}//QRectF TGraphicsSvgItem::boundingRect() const {
//    return shape().boundingRect();
//    return this->renderer()->boundsOnElement("shadow");
//}void TGraphicsSvgItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{if (!renderer()->isValid())return;if (elementId().isEmpty())renderer()->render(painter, boundingRect());elserenderer()->render(painter, elementId(), boundingRect());if(isSelected()){QPen pen2(QColor("#067BEF"), 2);pen2.setDashPattern(QVector<qreal>{2,2}); // 设置虚线模式pen2.setDashOffset(dashoffset);painter->setPen(pen2);painter->drawRect(shape().boundingRect()); // 绘制正方形}}QVariant TGraphicsSvgItem::itemChange(GraphicsItemChange change, const QVariant &value)
{if(change==QGraphicsSvgItem::ItemSelectedChange){if(value.toUInt()==1){dashoffset=100;if(timer!=nullptr && timer!=NULL)timer->start(100);}else{dashoffset=100;if(timer!=nullptr && timer!=NULL)timer->stop();}}return QGraphicsSvgItem::itemChange(change,value);
}

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

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

相关文章

11.1 Go 标准库的组成

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

Vue54-浏览器的本地存储webStorage

一、本地存储localStorage的作用 二、本地存储的代码实现 2-1、存储数据 注意&#xff1a; localStorage是window上的函数&#xff0c;所以&#xff0c;可以把window.localStorage直接写成localStorage&#xff08;直接调用&#xff01;&#xff09; 默认调了p.toString()方…

Linux中文件查找相关命令比较

Linux中与文件定位的命令有find、locate、whereis、which&#xff0c;type。 一、find find命令最强&#xff0c;能搜索各种场景下的文件&#xff0c;需要配合相关参数&#xff0c;搜索速度慢。在文件系统中递归查找文件。 find /path/to/search -name "filename"…

UniVue更新日志:使用ObservableList优化LoopList/LoopGrid组件的使用

github仓库 稳定版本仓库&#xff1a;https://github.com/Avalon712/UniVue 开发版本仓库&#xff1a;https://github.com/Avalon712/UniVue-Develop UniVue扩展框架-UniVue源生成器仓库&#xff1a;https://github.com/Avalon712/UniVue-SourceGenerator 更新说明 如果大家…

【电机控制】FOC算法验证步骤——PWM、ADC

【电机控制】FOC算法验证步骤 文章目录 前言一、PWM——不接电机1、PWMA-H-50%2、PWMB-H-25%3、PWMC-H-0%4、PWMA-L-50%5、PWMB-L-75%6、PWMC-L-100% 二、ADC——不接电机1.电流零点稳定性、ADC读取的OFFSET2.电流钳准备3.运放电路分析1.电路OFFSET2.AOP3.采样电路的采样值范围…

小白Linux提权

1.脏牛提权 原因&#xff1a; 内存子系统处理写入复制时&#xff0c;发生内存条件竞争&#xff0c;任务执行顺序异常&#xff0c;可导致应用崩溃&#xff0c;进一步执行其他代码。get_user_page内核函数在处理Copy-on-Write(以下使用COW表示)的过程中&#xff0c;可能产出竞态…

基于文本和图片输入的3D数字人化身生成技术解析

随着虚拟现实、增强现实和元宇宙等技术的飞速发展,对高度逼真且具有表现力的3D数字人化身的需求日益增长。传统的3D数字人生成方法往往需要依赖大量的3D数据集,这不仅增加了数据收集和处理的成本,还限制了生成的多样性和灵活性。为了克服这些挑战,我们提出了一种基于文本提…

Cocos Creator,Youtube 小游戏!

YouTube 官方前段时间发布了一则重磅通知&#xff0c;宣布平台旗下小游戏功能 Youtube Playables 正式登录全平台&#xff08;安卓、iOS、网页&#xff09;&#xff0c;并内置了数十款精选小游戏。 Youtube Playables 入口&#xff1a; https://www.youtube.com/playables Coco…

使用 C# 学习面向对象编程:第 7 部分

多态性 我们在程序中使用多态的频率是多少&#xff1f;多态是面向对象编程语言的第三大支柱&#xff0c;我们几乎每天都在使用它&#xff0c;却不去想它。 这是一个非常简单的图表&#xff0c;它将解释多态性本身。 简单来说&#xff0c;我们可以说&#xff0c;只要我们重载类…

【解决方案】数据采集工作站数据传不上去?

数据采集工作站扮演着至关重要的角色&#xff0c;它们负责收集、处理和传输各种传感器和设备的数据。然而&#xff0c;有时会遇到数据传输失败的问题。本文将详细探讨数据采集工作站数据传不上去的可能原因及其解决方案。&#xff08;更多了解采集器设备可前往苏州稳联&#xf…

【面试干货】Class.forName()与ClassLoader.loadClass()在Java反射中的区别

【面试干货】Class.forName&#xff08;&#xff09;与ClassLoader.loadClass&#xff08;&#xff09; 在Java反射中的区别 1、Class.forName()1.1 示例代码1.2 关键点 2、ClassLoader.loadClass()2.1 示例代码2.2 关键点 3、两者之间的区别 &#x1f496;The Begin&#x1f…

Training language models to follow instructions with human feedback 论文阅读

论文原文&#xff1a;https://arxiv.org/pdf/2203.02155 论文简介 语言模型越大并不意味着它能更好的理解用户的意图&#xff0c;因此在这篇论文中&#xff0c;展示了根据人的反馈对模型进行微调&#xff0c;使得语言模型能够在各种人物上更好的理解用户的意图。在评估中&…

【C++】模板进阶(特化)

&#x1f308;个人主页&#xff1a;秦jh_-CSDN博客&#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/qinjh_/category_12575764.html?spm1001.2014.3001.5482 目录 非类型模板参数 数组越界检查 按需实例化 模板的特化 函数模板特化 类模板特化 全特化 ​…

LabVIEW故障预测

在LabVIEW故障预测中&#xff0c;振动信号特征提取的关键技术主要包括以下几个方面&#xff1a; 时域特征提取&#xff1a;时域特征是直接从振动信号的时间序列中提取的特征。常见的时域特征包括振动信号的均值、方差、峰值、峰-峰值、均方根、脉冲指数等。这些特征能够反映振动…

【文末附gpt升级秘笈】AI热潮降温与AGI场景普及的局限性

AI热潮降温与AGI场景普及的局限性 摘要&#xff1a; 随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;AI热一度席卷全球&#xff0c;引发了广泛的关注和讨论。然而&#xff0c;近期一些学者和行业专家对AI的发展前景提出了质疑&#xff0c;认为AI热潮将逐渐…

如何警用root用户登录ssh

使用tail指令&#xff0c;可以动态查看日志信息。 &#xff08;tail -f /var/log/secure或messages&#xff09; 使用>符号&#xff0c;可以清空日志内容&#xff0c;不删除文件本身。 禁用root用户为以下步骤&#xff1a; 首先使用useradd创建用户&#xff08;可以修改为其…

路由器虚拟服务器有什么作用

现如今在IPv4时代&#xff0c;由于公网IP地址的匮乏&#xff0c;约有70%的电脑都处于内网中&#xff0c;上网需要通过路由器。如果反过来想要访问身处内网的电脑&#xff0c;我们就需要在路由器里开放相应的端口才能实现。而这开放端口的功能&#xff0c;在路由器里就叫做虚拟服…

俄罗斯Yandex推广投放如何开户?Yandex广告开户和代运营推广流程详解_俄罗斯_受众_搜索引擎

在俄罗斯进行Yandex广告推广是一种有效的在线营销方式&#xff0c;特别是针对俄罗斯市场。Yandex是俄罗斯最受欢迎的搜索引擎&#xff0c;类似于Google在全球范围内的地位。以下是通过Yandex广告推广的一般步骤&#xff0c;以及如何通过上海上弦进行广告开户和代运营。 1. Yan…

GPT_AI高速发展中什么是Prompt提示词?

提示词&#xff08;Prompt&#xff09;是给大语言模型&#xff08;以下简称模型&#xff09;的输入文本&#xff0c;用于指定模型应该执行什么样的任务并生成什么样的输出。 提示词发挥了“提示” 模型 应该做什么的作用。设计高质量的提示词需要根据目标任务和模型能力进行精…

49.Python-web框架-Django解决多语言redirect时把post改为get的问题

目录 1.背景 2.思路 3.寻找 Find and Replace 4.再次运行程序&#xff0c;POST来了 5.小结 1.背景 昨天在练习一个Django功能时&#xff0c;把form的method设置为POST&#xff0c;但是实际提交时&#xff0c;一直是GET方法。最后发现这是与多语言相关&#xff0c;django前面…