《QT实用小工具·五十》动态增删数据与平滑缩放移动的折线图

news2024/10/5 15:31:48

1、概述
源码放在文章末尾

该项目实现了带动画、带交互的折线图,包含如下特点:
动态增删数值
自适应显示坐标轴数值
鼠标悬浮显示十字对准线
鼠标靠近点自动贴附
支持直线与平滑曲线效果
自定义点的显示类型与大小
自适应点的数值显示位置
根据指定锚点缩放
平滑的横向移动
选中的纵向渐变效果

项目demo演示如下所示:
在这里插入图片描述

项目部分代码如下所示:

#ifndef LINECHART_H
#define LINECHART_H

#include <QObject>
#include <QWidget>
#include <QList>
#include <QPainter>
#include <QPainterPath>
#include <QPropertyAnimation>
#include <QtMath>

struct ChartData
{
    QString title;
    QColor color = Qt::black;
    int xMin = 0;
    int xMax = 0;
    int yMin = 0;
    int yMax = 0;
    QList<QPoint> points;
    QList<QString> xLabels; // X显示的名字,可空,比如日期
};

struct Vector2D : public QPointF
{
    Vector2D(double x, double y) : QPointF(x, y)
    {
    }

    Vector2D(QPointF p) : QPointF(p)
    {
    }

    /// 向量长度
    double length()
    {
        return sqrt(x() * x() + y() * y());
    }

    /// 转单位向量
    Vector2D normalize()
    {
        double len = length();
        double inv;
        if (len < 1e-4)
            inv = 0;
        else
            inv = 1 / length();
        return Vector2D(x() * inv, y() * inv);
    }

    /// 向量相加
    Vector2D operator+ (Vector2D v)
    {
        return Vector2D(x() + v.x(), y() + v.y());
    }

    /// 向量翻倍
    Vector2D operator* (double f)
    {
        return Vector2D(x() * f, y() * f);
    }

    /// 内积
    double dot(Vector2D v)
    {
        return x() * v.x() + y() * v.y();
    }

    /// 两个向量夹角
    double angle(Vector2D v)
    {
        return acos(dot(v) / (length() * v.length())) * 180 / M_PI;
    }
};

class LineChart : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(int display_x_min READ getDisplayXMin WRITE setDisplayXMin)
    Q_PROPERTY(int display_x_max READ getDisplayXMax WRITE setDisplayXMax)
    Q_PROPERTY(int display_y_min READ getDisplayYMin WRITE setDisplayYMin)
    Q_PROPERTY(int display_y_max READ getDisplayYMax WRITE setDisplayYMax)

public:
    LineChart(QWidget *parent = nullptr);

    int lineCount() const;
    void setPointLineType(int t);
    void setPointValueType(int t);
    void setPointDotType(int t);
    void setPointDotRadius(int r);
    void setLabelSpacing(int s);

    void addLine(ChartData data);
    void removeLine(int index);
    void addPoint(int index, int x, int y);
    void addPoint(int index, int x, int y, const QString& label);
    void removeFirst(int index);

    void updateAnchors();
    void zoom(double prop);
    void moveHorizontal(int x);

signals:
    void signalSelectRangeChanged(int start, int end);

public slots:
    void zoomIn();
    void zoomOut();

protected:
    void paintEvent(QPaintEvent *event) override;
    void enterEvent(QEvent *event) override;
    void leaveEvent(QEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void wheelEvent(QWheelEvent *event) override;

private:
    void setDisplayXMin(int v);
    int getDisplayXMin() const;
    void setDisplayXMax(int v);
    int getDisplayXMax() const;
    void setDisplayYMin(int v);
    int getDisplayYMin() const;
    void setDisplayYMax(int v);
    int getDisplayYMax() const;

    void saveRange();
    void startRangeAnimation();
    QPropertyAnimation* startAnimation(const QByteArray &property, int start, int end, bool* flag, int duration = 300, QEasingCurve curve = QEasingCurve::OutQuad);

    int getValueByCursorPos(QPoint pos);

private:
    // 数据
    QList<ChartData> datas;                 // 所有折线的数据

    // 界面
    QRect contentRect;                      // 显示的范围,实时刷新
    QRect paddings = QRect(32, 32, 32, 32); // 四周留白(width=right,height=bottom)
    QColor borderColor = Qt::gray;          // 边界线颜色
    int labelSpacing = 2;                   // 标签间距

    // 信息显示
    bool autoResize = true;                 // 自动调整大小
    int displayXMin = 0, displayXMax = 0;   // 显示的X轴范围
    int displayYMin = 0, displayYMax = 0;   // 显示的Y轴范围
    bool usePointXLabels = true;            // 优先使用点对应的label,还是相同间距的数值
    QList<QString> xLabels;                 // 显示的文字(可能少于值数量)
    QList<int> xLabelPoss;
    int pointLineType = 3;                  // 连线类型:1直线,2二次贝塞尔曲线,3三次贝塞尔曲线(更精确但吃性能)
    int pointValueType = 2;                 // 数值显示位置:0无,1强制上方,2自动附近
    int pointDotType = 1;                   // 圆点类型:0无,1空心圆,2实心圆,3小方块
    int pointDotRadius = 2;                 // 圆点半径

    // 动画效果
    bool enableAnimation = true;
    int _savedXMin, _savedXMax;             // 修改前的数值
    int _savedYMin, _savedYMax;
    bool animatingXMin = false, animatingXMax = false; // 是否正在动画中
    bool animatingYMin = false, animatingYMax = false;
    int _animatedXMin, _animatedXMax;       // 动画中的数值(仅影响显示)
    int _animatedYMin, _animatedYMax;

    // 交互数据
    bool pressing = false;
    QPoint pressPos, releasePos;
    bool hovering = false;
    QPoint hoverPos;
    int nearDis = 8;                        // 四周这些距离内算是“附近”

    // 悬浮提示
    bool showCrossOnPressing = true;        // 按下显示十字对准线
    QColor hightlightColor = QColor("#FF7300");       // 高亮颜色

    // 鼠标选择
    bool enableSelect = true;
    bool selecting = false;
    int selectPos = 0;                      // 最后一次鼠标点击的X像素(相对显示矩形)
    int selectXStart = 0, selectXEnd = 0;   // 鼠标按下/松开的对应X值位置
    QColor selectColor = QColor("#F08080"); // 选择区域颜色

    // 缩放(仅针对X轴)
    bool enableScale = true;
    int displayXStart = 0, displayXEnd = 0;
};

#endif // LINECHART_H

源码下载

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

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

相关文章

程序包的创建

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 前面很多范例中都用到的 dbms output.put_line 实际上就是一个典型的程序包应用&#xff0c; 其中 dbms output是程序包的名称&#xff0c;put_line 是该程序包中定义的一个…

Python-快速搭建一个管理平台

目录 &#x1f4dc; 准备工作 一、项目介绍 ✨ 二、制作数据库表 添加信息 ⚒️ 三、运行client.exe &#x1f680; 1、连接数据库&#xff0c;选择对应表&#xff0c;生成代码 2、把后端代码依次复制到项目中 3、把前端代码依次复制到前端项目中 4、添加路由 四、运行后端项目…

异地组网,让“远程运维”更简单

您是否在联网场景中有过这些需求&#xff1f; 摄像头需要联网统一监控、PLC需要联网告别本地升级、工控机需要联网告别本地配置、广告屏需要联网告别本地下载视频、远程打开终端设备WEB进行配置......这些问题有人新升级的“异地组网”功能统统可以解决&#xff01; 告别繁琐…

【Unity】修改模型透明度

在 Unity 中修改模型透明度主要有两种方法&#xff1a;通过材质和通过着色器。以下是两种方法的步骤和解释&#xff1a; 方法 1&#xff1a;通过材质 在 Unity 编辑器中&#xff0c;选择你想要修改透明度的模型。在 Inspector 窗口中&#xff0c;找到模型的 Renderer 组件&am…

Java | Leetcode Java题解之第62题不同路径

题目&#xff1a; 题解&#xff1a; class Solution {public int uniquePaths(int m, int n) {long ans 1;for (int x n, y 1; y < m; x, y) {ans ans * x / y;}return (int) ans;} }

Linux 进程间通信之匿名管道

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;Linux知识分享⏪   &#x1f69a;代码仓库:Linux代码练习&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Linux知识   &#x1f51d; 目录 前言 一. 进程间通信介绍 1.进程间通…

MySQL8密码复杂度设置

在MySQL中&#xff0c;密码复杂度设置可以通过调整密码验证插件的配置参数来实现。MySQL 8.0 引入了 validate_password 插件&#xff0c;用于强制实施密码策略。 查询是否已安装的插件&#xff1a;show plugins;确认是否已安装插件validate_password 查询mysql插件目录位置&…

RTMP 直播推流 Demo(二)—— 音频推流与视频推流

音视频编解码系列目录&#xff1a; Android 音视频基础知识 Android 音视频播放器 Demo&#xff08;一&#xff09;—— 视频解码与渲染 Android 音视频播放器 Demo&#xff08;二&#xff09;—— 音频解码与音视频同步 RTMP 直播推流 Demo&#xff08;一&#xff09;—— 项目…

【最大公约数 调和级数】2183.统计可以被 K 整除的下标对数目

本文涉及知识点 最大公约数 调和级数 LeetCode2183. 统计可以被 K 整除的下标对数目 给你一个下标从 0 开始、长度为 n 的整数数组 nums 和一个整数 k &#xff0c;返回满足下述条件的下标对 (i, j) 的数目&#xff1a; 0 < i < j < n - 1 且 nums[i] * nums[j] 能…

读天才与算法:人脑与AI的数学思维笔记15_声响的数学之旅

1. 音乐 1.1. 巴赫的作品以严格的对位著称&#xff0c;他十分中意对称的结构 1.2. 巴托克的作品很多都以黄金比例为结构基础&#xff0c;他非常喜欢并善于使用斐波纳契数列 1.3. 有时&#xff0c;作曲家是本能地或者不自知地被数学的模式和结构所吸引&#xff0c;而他们并没…

MySQL-分页查询

MySQL分页查询 MySQL 分页查询原则&#xff1a; 在 MySQL 数据库中使用 LIMIT 子句进行分页查询。MySQL 分页中开始位置为 0。分页子句在查询语句的最后侧。 LIMIT子句 SELECT 投影列 FROM 表名 WHERE 条件 ORDER BY LIMIT 开始位置&#xff0c;查询数量;示例&#xff1a; …

【ARMv8/v9 系统寄存 3 -- system counter CNTPCT_EL0】

文章目录 ARMv8/v9 system countersystem counter读取函数实现 ARMv8/v9 system counter 所有使用Arm处理器的系统中都会包含一个标准化的通用定时器&#xff08;Generic Timer&#xff09;框架。这个通用定时器系统提供了一个系统计数器&#xff08;System Counter&#xff0…

模块六:模拟——1419.数青蛙

文章目录 题目描述算法原理解法&#xff08;模拟 分情况讨论&#xff09; 代码实现 题目描述 题目链接&#xff1a;1419.数青蛙 算法原理 解法&#xff08;模拟 分情况讨论&#xff09; 模拟⻘蛙的叫声。 当遇到 ‘r’ ‘o’ ‘a’ ‘k’ 这四个字符的时候&#xff0c;我…

Redis__数据持久化

文章目录 &#x1f60a; 作者&#xff1a;Lion J &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_69252724 &#x1f389; 主题&#xff1a;Redis__数据持久化 ⏱️ 创作时间&#xff1a;2024年05月01日 ———————————————— 这里写目录标题…

Qt | QFrame容器

01、QFrame 一、QFrame 类 1、QFrame类是带有边框的部件的基类,带边框部件的特点是有一个明显的边框,QFrame 类就是用来实现边框的不同效果的(把这种效果称为边框样式),所有继承自 QFrame 的子 类都可以使用 QFrame 类实现的效果。 2、部件通常是矩形的(其他形状的原理…

O32系统学习

O32系统学习 一、什么是O32系统 O32系统即基金投资管理系统&#xff0c;最开始是为基金公司开发的投资交易管理系统&#xff0c;到后来逐步涉及到券商、券商资管、保险、信托、期货&#xff0c;私募&#xff0c;甚至财务公司&#xff0c;金融控股集团&#xff0c;非标交易所等…

Apollo:开源多语言医疗大型语言模型

前言 医疗知识的整合与人工智能一直是研究界的焦点&#xff0c;每一点进步都可能带来更好的患者体验和更高的治愈率。尽管医疗大型语言模型(LLM)前景广阔&#xff0c;但现有工作主要集中在中文和英文上&#xff0c;对于其他语言的多语言适配还有待进一步探索。 为了将最先进的…

[华为OD]C卷 机场航班调度 ,XX市机场停放了多架飞机,每架飞机都有自己的航班号100

题目&#xff1a; XX市机场停放了多架飞机&#xff0c;每架飞机都有自己的航班号CA3385, CZ6678, SC6508 等&#xff0c;航班号的前2个大写字母&#xff08;或数字&#xff09;代表航空公司的缩写&#xff0c;后面4个数字代表航班信息。 但是XX市机场只有一条起飞用跑道&am…

使用ipxe安装现有的装机环境

iPXE和传统PXE区别 iPXE和传统PXE&#xff08;Pre-boot Execution Environment&#xff0c;预启动执行环境&#xff09;的主要区别在于它们的功能和协议支持。以下是两者的主要区别&#xff1a; 协议支持&#xff1a; PXE仅支持TFTP&#xff08;trivial file transfer protoco…

【精选文献】JAG|基于时序Sentinel-1 SAR影像小农耕作区烟草空间分布制图

目录 文章简介 01 文章摘要 02 研究背景、目标及创新点 03 研究区域与数据集 04 研究方法 05 研究结果 06 研究讨论 07 研究结论 08 文章引用 文章简介 论文名称&#xff1a;Mapping tobacco planting areas in smallholder farmlands using Phenological-Spatial-Te…