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

news2025/1/16 14:08:05

目录导读

    • 简述
      • 使用 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 pen

    const QColor fgcolor = option->palette.windowText().color();
    const QColor bgcolor( // ensure good contrast against fgcolor
        fgcolor.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());
     else
         renderer()->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());
        else
            renderer()->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.coloradmin.cn/o/1853594.html

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

相关文章

物联网系统运维——移动电商服务器单点部署,web服务器部署,Nginx Web服务介绍,Nginx性能,部署,架构,及实验:安装并设置Nginx(重点)

一.web服务器介绍 Web服务器一般指网站服务器&#xff0c;是指驻留于因特网上提供某种特定类型计算机的程序&#xff0c;Web服务器可以向浏览器等Web客户端提供文档&#xff0c;也可以放置网站文件&#xff0c;让全世界浏览&#xff0c;可以放置数据文件&#xff0c;让全世界下…

七种常见的前端攻击

随着网络应用程序在商业运作中的重要性日益增加&#xff0c;它们也成为了网络攻击的更具吸引力的目标。不幸的是&#xff0c;与后端和 DevOps 团队相比&#xff0c;许多前端开发人员在构建安全前端方面已经落后了。这种差距增加了破坏性数据泄露的风险。 最近的事件&#xff0…

Linux_软硬链接

目录 1、软链接 2、软链接的使用方式 3、软链接的删除 4、硬链接 5、硬链接的使用方式 6、软硬链接的使用场景 7、软硬链接的区别 结语 前言&#xff1a; 在Linux操作系统中&#xff0c;有软链接和硬链接&#xff0c;他们是一种特殊的文件引用&#xff0c;主要用于与…

AGV机器人的调度开发分析(1)- 内核中的路线规划

准备开始写一个系列&#xff0c;介绍下AGV机器人的调度的开发和应用。 按照openTCS的核心内容&#xff0c;国内多家广泛应用于AGV的调度。那么架构图如下&#xff1a; Kernel中有一个是Routing&#xff0c;这是路由规划模块&#xff0c;需要实现的细节功能包括如下&#xff1a…

理解什么是DSR,嗅探器视角下的IP和MAC地址识别(C/C++代码实现)

网络嗅探器是监控和分析网络流量的一种工具&#xff0c;它能够捕获数据包并提取出关键的信息&#xff0c;比如IP地址和MAC地址。 网络嗅探器工作原理基于网卡的工作模式。正常情况下&#xff0c;网卡只处理发送给它的数据包&#xff0c;忽略其他数据。但是&#xff0c;如果将网…

Java程序之简单“记事本”

要求&#xff1a;如下图所示&#xff0c;记事本具有新建、打开文本、保存、关闭等功能。 算法思路&#xff1a; 这是一个使用Java Swing库创建的简单文本编辑器。它包含一个菜单栏&#xff0c;其中包含“文件”菜单以及四个子菜单项&#xff1a;“新建”、“打开”、“保存”和…

机器学习课程复习——集成学习

1. 基本概念 1.1. 定义 通过构建并结合多个个体学习器来完成学习任务,获得比单一学习器显著优越的泛化性能。 1.2. 分类 名称个体学习器例子同质集成基学习器Boosting、Bagging异质集成组件学习器Stacking1.3. 研究的核心 个体学习器的“准确性”和“多样性”本身就存在冲…

二叉树经典OJ练习

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 二叉树经典OJ练习 收录于专栏【数据结构初阶】 本专栏旨在分享学习数据结构学习的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 前置说…

三、MyBatis实践:提高持久层数据处理效率

三、MyBatis实践&#xff1a;提高持久层数据处理效率 目录 一、Mybatis简介 1.1 简介1.2 持久层框架对比1.3 快速入门&#xff08;基于Mybatis3方式&#xff09; 二、MyBatis基本使用 2.1 向SQL语句传参 2.1.1 mybatis日志输出配置2.1.2 #{}形式2.1.3 ${}形式 2.2 数据输入 2…

cesium 包络线

cesium 包络线 以下为源码直接复制可用 1、实现思路 通过turf.js中union方法来计算包络线官方地址:https://turfjs.fenxianglu.cn/ 闪烁线请查看cesium轨迹线(闪烁轨迹线) 2、示例代码 <!DOCTYPE html> <html lang="en"&g

SpringBoot配置第三方专业缓存技术Memcached 下载 安装 整合测试 2024年5000字详解

Memcached下载和安装 是一个国内使用量还是比较大的技术 打开文件夹 我们需要在命令行窗口启动 注意要以管理员方式运行 先尝试进入指定文件 然后又再次运行 下载 memcached.exe -d install 启动 memcached.exe -d start 停止 memcached.exe -d stop memcached.exe -d i…

Mysql数据库约束的概述 , 逐渐约束 , 主键自增 , 表关系的概念和外键 ,多表关系约束介绍和使用

约束和表设计 1、DQL查询语句-limit语句(掌握) 目标 能够掌握limit语句的使用 讲解 作用&#xff1a; LIMIT是限制的意思&#xff0c;所以LIMIT的作用就是限制查询记录的条数。 LIMIT语句格式: select * from 表名 limit offset, row_count; mysql中limit的用法&#…

轻松恢复丢失数据EasyRecovery你的数据守护神

数据丢失&#xff1f;别怕&#xff01;EasyRecovery来帮忙 大家好呀&#xff0c;今天我要分享一个我超级喜欢的数据恢复软件——EasyRecovery&#xff01;&#x1f389; 如果你也经历过误删文件、硬盘格式化或是意外丢失重要数据的尴尬和焦虑&#xff0c;那你一定要看看这个神器…

Bytebase 对接本地部署的 llama3 开启ChatSQL功能

Bytebase 是为开发人员、测试、DBA和运维工程师构建的数据库 DevOps 领域的&#xff0c;类 GitLab/GitHub 平台。 这篇文章主要关注 Bytebase SQL 编辑器中的 AI 增强功能。使用此功能您可以使用自然语言在 Bytebase SQL 编辑器中查询数据库。同时还能给出针对查询的索引建议&…

千呼新零售2.0【更新日志】持续更新ing

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货等连锁店使用。 详细介绍请查看下…

【CSS in Depth 2 精译】1.5 渐进式增强

文章目录 1.5 渐进式增强1.5.1 利用层叠规则实现渐进式增强1.5.2 渐进式增强的选择器1.5.3 利用 supports() 实现特性查询启用浏览器实验特性 1.5 渐进式增强 要用好 CSS 这样一门不断发展演进中的语言&#xff0c;其中一个重要的因素就是要与时俱进&#xff0c;及时了解哪些功…

HTML静态网页成品作业(HTML+CSS)——动漫猪猪侠网页(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…

测试辅助工具(抓包工具)的使用4 之 断点

抓包作用3&#xff08;绕过界面限制测试&#xff09; 1.为什么要绕过界面限制做测试&#xff1f; 原因&#xff1a;界面限制导致部分异常数据无法输入 2.如何绕过界面限制做测试&#xff1f; 绕过界面限制直接测试服务器 步骤&#xff1a; 1.设置断点 2.修改请求 3.修改响应…

微型操作系统内核源码详解系列五(五):cm3下Pendsv切换任务上篇

系列一&#xff1a;微型操作系统内核源码详解系列一&#xff1a;rtos内核源码概论篇&#xff08;以freertos为例&#xff09;-CSDN博客 系列二&#xff1a;微型操作系统内核源码详解系列二&#xff1a;数据结构和对象篇&#xff08;以freertos为例&#xff09;-CSDN博客 系列…

React的路由(ReactRouter)-路由导航跳转

1.第一步 // createBrowserRouter路由 RouterProvider组件 import {createBrowserRouter,RouterProvider} from react-router-dom // 创建router实例对象&#xff0c;并配置路由对应关系 const routercreateBrowserRouter([{path:/login,element:<div>我是登录页</di…