QT实现曲线图缩放、拖拽以及框选放大

news2025/5/9 19:34:59

.h文件

protected:    
    void saveAxisRange();
    void wheelEvent(QWheelEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;

private:
    QPoint m_lastPoint;
    bool m_isPress = false;  //这里我把平移的判定初始化成了false,解决鼠标一移进QchartView就跟随移动的情况
    bool m_ctrlPress;
    bool m_alreadySaveRange;
    double m_xMin, m_xMax, m_yMin, m_yMax;
    QGraphicsSimpleTextItem* m_coordItem;

    // 框选
    bool m_isSelecting = false;        // 是否正在框选
    QPoint m_selectionStart;           // 框选起点
    QGraphicsRectItem* m_selectionRect = nullptr; // 选框图形项

.cpp文件

void saveAxisRange()
{
    QValueAxis *axisX = dynamic_cast<QValueAxis*>(this->chart()->axisX());
    m_xMin = axisX->min();
    m_xMax = axisX->max();
    QValueAxis *axisY = dynamic_cast<QValueAxis*>(this->chart()->axisY());
    m_yMin = axisY->min();
    m_yMax = axisY->max();
}

void wheelEvent(QWheelEvent *event)
{
     获取当前轴的缩放因子(0.9为缩小,1.1为放大)
    //const double zoomFactor = 0.9;
    //const double minRange = 0.01; // 防止缩放到负数或过小

     获取当前轴范围
    //QValueAxis* axisX = qobject_cast<QValueAxis*>(chart()->axes(Qt::Horizontal).first());
    //QValueAxis* axisY = qobject_cast<QValueAxis*>(chart()->axes(Qt::Vertical).first());
    //if(!axisX || !axisY)
    //{
    //    return;
    //}

     计算缩放方向(向上滚为缩小,向下滚为放大)
    //double delta = event->angleDelta().y() > 0 ? zoomFactor : 1.0 / zoomFactor;

     根据修饰键选择缩放轴
    //if(event->modifiers() & Qt::ControlModifier)
    //{
    //    // 缩放Y轴
    //    double newMinY = axisY->min() * delta;
    //    double newMaxY = axisY->max() * delta;
    //    if(newMaxY - newMinY > minRange)
    //    {
    //        axisY->setRange(newMinY, newMaxY);
    //    }
    //}
    //else
    //{
    //    // 缩放X轴
    //    double newMinX = axisX->min() * delta;
    //    double newMaxX = axisX->max() * delta;
    //    if(newMaxX - newMinX > minRange)
    //    {
    //        axisX->setRange(newMinX, newMaxX);
    //    }
    //}

    //event->accept();

    const QPoint curPos = event->pos();
    QPointF curVal = this->chart()->mapToValue(QPointF(curPos));

    if(!m_alreadySaveRange)
    {
        this->saveAxisRange();
        m_alreadySaveRange = true;
    }
    const double factor = 1.5;//缩放比例
    if(event->modifiers() & Qt::ControlModifier)
    {
        //Y轴
        QValueAxis *axisY = dynamic_cast<QValueAxis*>(this->chart()->axisY());
        const double yMin = axisY->min();
        const double yMax = axisY->max();
        const double yCentral = curVal.y();

        double bottomOffset;
        double topOffset;
        if(event->delta() > 0)
        {
            //放大
            bottomOffset = 1.0 / factor * (yCentral - yMin);
            topOffset = 1.0 / factor * (yMax - yCentral);
        }
        else
        {
            //缩小
            bottomOffset = 1.0 * factor * (yCentral - yMin);
            topOffset = 1.0 * factor * (yMax - yCentral);
        }

        this->chart()->axisY()->setRange(yCentral - bottomOffset, yCentral + topOffset);
    }
    else
    {
        //X轴
        QValueAxis *axisX = dynamic_cast<QValueAxis*>(this->chart()->axisX());
        const double xMin = axisX->min();
        const double xMax = axisX->max();
        const double xCentral = curVal.x();

        double leftOffset;
        double rightOffset;
        if(event->delta() > 0)
        {
            //放大
            leftOffset = 1.0 / factor * (xCentral - xMin);
            rightOffset = 1.0 / factor * (xMax - xCentral);
        }
        else
        {
            //缩小
            leftOffset = 1.0 * factor * (xCentral - xMin);
            rightOffset = 1.0 * factor * (xMax - xCentral);
        }
        this->chart()->axisX()->setRange(xCentral - leftOffset, xCentral + rightOffset);
    }
}

void mousePressEvent(QMouseEvent *event)
{
    //if(event->button() == Qt::LeftButton)
    //{
    //    m_lastPoint = event->pos();
    //    m_isPress = true;
    //}
    if(event->button() == Qt::LeftButton)
    {
        // 检测 Ctrl 键是否按下
        if(QApplication::keyboardModifiers() & Qt::ControlModifier)
        {
            // 开始框选
            m_isSelecting = true;
            m_selectionStart = event->pos();

            // 创建选框图形项
            if(!m_selectionRect)
            {
                m_selectionRect = new QGraphicsRectItem();
                m_selectionRect->setPen(QPen(Qt::blue, 1, Qt::DashLine));
                m_selectionRect->setBrush(QColor(100, 100, 255, 50));
                chart()->scene()->addItem(m_selectionRect);
            }
        }
        else
        {
            // 正常平移模式
            m_lastPoint = event->pos();
            m_isPress = true;
        }
    }
}

void mouseMoveEvent(QMouseEvent *event)
{
    /*if(!m_coordItem)
    {
        m_coordItem = new QGraphicsSimpleTextItem(this->chart());
        m_coordItem->setZValue(5);
        m_coordItem->setPos(100, 60);
        m_coordItem->show();
    }
    const QPoint curPos = event->pos();

    if(m_isPress)
    {
        QPoint offset = curPos - m_lastPoint;
        m_lastPoint = curPos;
        if(!m_alreadySaveRange)
        {
            this->saveAxisRange();
            m_alreadySaveRange = true;
        }
        this->chart()->scroll(-offset.x(), offset.y());
    }*/

    // 更新坐标显示(原功能保留)
    if(!m_coordItem)
    {
        m_coordItem = new QGraphicsSimpleTextItem(chart());
        m_coordItem->setZValue(5);
        m_coordItem->setPos(100, 60);
    }

    // 处理框选逻辑
    if(m_isSelecting)
    {
        // 更新选框图形
        QRectF rect(m_selectionStart, event->pos());
        m_selectionRect->setRect(rect.normalized());
    }
    // 处理平移逻辑
    else if(m_isPress)
    {
        QPoint offset = event->pos() - m_lastPoint;
        m_lastPoint = event->pos();
        if(!m_alreadySaveRange)
        {
            saveAxisRange();
            m_alreadySaveRange = true;
        }
        chart()->scroll(-offset.x(), offset.y());
    }
}

void mouseReleaseEvent(QMouseEvent *event)
{
    //m_isPress = false;
    //if(event->button() == Qt::RightButton)
    //{
    //    if(m_alreadySaveRange)
    //    {
    //        this->chart()->axisX()->setRange(m_xMin, m_xMax);
    //        this->chart()->axisY()->setRange(m_yMin, m_yMax);
    //    }
    //}

    if(event->button() == Qt::LeftButton)
    {
        if(m_isSelecting)
        {
            // 应用框选缩放
            QRectF rect = m_selectionRect->rect();
            QPointF topLeft = chart()->mapToValue(rect.topLeft());
            QPointF bottomRight = chart()->mapToValue(rect.bottomRight());

            chart()->axisX()->setRange(topLeft.x(), bottomRight.x());
            chart()->axisY()->setRange(bottomRight.y(), topLeft.y()); // Y轴方向反转

            // 清理选框
            chart()->scene()->removeItem(m_selectionRect);
            delete m_selectionRect;
            m_selectionRect = nullptr;
            m_isSelecting = false;
        }
        else
        {
            m_isPress = false;
        }
    }
    // 原右键恢复功能保留
    else if(event->button() == Qt::RightButton)
    {
        if(m_alreadySaveRange)
        {
            chart()->axisX()->setRange(m_xMin, m_xMax);
            chart()->axisY()->setRange(m_yMin, m_yMax);
        }
    }
}

构造函数中添加代码:

    grabGesture(Qt::PinchGesture);                              //这里只grabGesture了PinchGesture

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

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

相关文章

科学发现 | 源于生活的启示与突破计划的创新

注&#xff1a;本文为“科学发现”相关文章合辑。 略作重排&#xff0c;未全整理。 哪些重大科学发现&#xff0c;来自生活的启示 ︱ 科学史 2020/10/29 导读 好奇心是最好的向导和老师。 撰文 | 陈敬全&#xff08;东华大学人文学院教授&#xff09; 英国进化论者赫胥黎…

【ArcGIS微课1000例】0145:如何按照自定义形状裁剪数据框?

文章目录 一、添加数据二、绘制形状三、裁剪格网和经纬网一、添加数据 打开软件,添加配套实验数据包中0145.rar中的影像数据,如下图所示: 二、绘制形状 1. 在数据视图中,使用绘图 工具条上的新建圆工具 可创建一个椭圆,使其包含要在该数据框中显示的数据范围。 修改椭圆…

网络安全防火墙技术有哪些?网络防火墙的主要作用

网络安全防火墙技术有哪些?网络防火墙的主要作用 网络安全防火墙技术是保护网络免受未经授权访问和攻击的关键工具。以下是常见的防火墙技术及其主要作用&#xff1a; 一、网络安全防火墙技术分类 包过滤防火墙&#xff08;Packet Filtering Firewall&#xff09; 原理&#x…

数据集-目标检测系列- 印度人脸 检测数据集 indian face >> DataBall

数据集-目标检测系列- 印度人脸 检测数据集 indian face >> DataBall DataBall 助力快速掌握数据集的信息和使用方式。 贵在坚持&#xff01; * 相关项目 1&#xff09;数据集可视化项目&#xff1a;gitcode: https://gitcode.com/DataBall/DataBall-detections-100s…

Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.

版本: 原因 在 Dart Sass 3.0.0 中, @import 规则将被弃用,推荐使用 @use 和 @forward 规则来替代。 1.@use替代@import @use 规则允许你引入其他 Sass 文件中的变量、混合器和函数,并且可以避免命名冲突。 示例: style.scss @use variables;body {color: variables.$pr…

【工具教程】批量提取PDF指定内容并重命名,PDF文档根据指定识别文字改名,基于java的实现方案

物流单据处理​​&#xff1a;每天处理大量发货单PDF&#xff0c;提取订单编号、发货方信息等关键字段重命名文件 合同管理​​&#xff1a;从合同PDF中提取合同编号、签署方名称等作为文件名 ​​学术论文整理​​&#xff1a;根据论文标题或作者信息重命名PDF文件 财务票据…

苍穹外卖(用户下单、订单支付)

用户下单、订单支付 导入地址簿功能代码 接口设计 数据库设计&#xff08;address_book表&#xff09; 代码导入 功能测试 用户下单 接口设计 数据库设计 订单表 orders 订单明细表 order_detail 代码开发 根据用户下单接口的参数设计DTO 根据用户下单接口的…

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】3.2 缺失值检测与处理(NULL值填充/删除策略)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 缺失值检测与处理全攻略&#xff1a;NULL值填充与删除策略实战3.2 缺失值检测与处理3.2.1 缺失值类型与业务影响3.2.1.1 缺失值的三种形态3.2.1.2 业务影响分级 3.2.2 缺失值…

2025年渗透测试面试题总结-某步在线面试(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 一、操作系统相关问题总结与分析及扩展回答 1. Linux命令熟悉度 2. 查看进程的命令 3. 查看网络进程…

Java后端程序员学习前端之JavaScript

1.什么是JavaScript 1.1.概述 JavaScript是一门世界上最流行的脚本语言javaScript 一个合格的后端人员&#xff0c;必须要精通JavaScript 1.2.历史 JavaScript的起源故事-CSDN博客 2.快速入门 2.1.引入JavaScript 1.内部标签 <script>//.......</script> --…

uniapp-商城-43-shop 后台管理 页面

后台管理较为简单&#xff0c;主要用于后台数据的管理&#xff0c;包含商品类别和商品信息&#xff0c;其实还可以扩展到管理用户等等 1、后台首页 包含 分类管理 商品管理 关于商家等几个栏目 主要代码&#xff1a; <template><view class"manage">…

vue2 结合后端预览pdf 跨域的话就得需要后端来返回 然后前端呈现

<el-button :loading"pdfIslock" v-if"isPDFFile(form.pic)" type"primary" style"margin: 15px 0" click"previewPDF(form.pic)"> 预览pdf </el-button>//npm install pdfjs-dist //如果没有就得先安装import …

什么是 HSQLDB?

大家好&#xff0c;这里是架构资源栈&#xff01;点击上方关注&#xff0c;添加“星标”&#xff0c;一起学习大厂前沿架构&#xff01; Java开发人员学习Java数据库连接&#xff08;JDBC&#xff09;的最简单方法是试验HyperSQL数据库&#xff08;又名HSQLDB&#xff09;。 …

多语言爬虫实现网站价格监控

最近突发奇想想用多种代码来爬取数据做价格监控。常见的比如Python、JavaScript(Node.js)、或者Go&#xff1f;不过通常来说&#xff0c;Python应该是首选&#xff0c;因为它的库比较丰富&#xff0c;比如requests和BeautifulSoup&#xff0c;或者Scrapy。不过客户要求多种代码…

16.Three.js 中的 RectAreaLight 全面详解 + Vue 3 实战案例

&#x1f60e; 本文将带你从零了解 THREE.RectAreaLight 的工作原理、使用方式、注意事项&#xff0c;并在最后用 Vue 3 的 Composition API 封装一个完整的光源演示组件&#xff0c;一站式搞懂矩形区域光的魅力 &#x1f4a1;&#xff01; &#x1f5bc;️ 一、展示图效果示意…

excel 批量导出图片并指定命名

一、开发环境 打开excel文件中的宏编辑器和JS代码调试 工具-》开发工具-》WPS宏编辑器 左边是工程区&#xff0c;当打开多个excel时会有多个&#xff0c;要注意不要把代码写到其他工作簿去了 右边是代码区 二、编写代码 宏是js语言&#xff0c;因此变量或者方法可以网上搜…

Mem0.ai研究团队开发的全新记忆架构系统“Mem0”正式发布

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

通过DeepSeek大语言模型控制panda机械臂,听懂人话,拟人性回答。智能机械臂助手又进一步啦

文章目录 前言环境配置运行测试报错 前言 通过使用智能化的工作流控制系统来精确操控机械臂&#xff0c;不仅能够基于预设算法可靠地规划每个动作步骤的执行顺序和力度&#xff0c;确保作业流程的标准化和可重复性&#xff0c;还能通过模块化的程序设计思路灵活地在原有工作流中…

如何添加或删除极狐GitLab 项目成员?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 项目成员 (BASIC ALL) 成员是有权访问您的项目的用户和群组。 每个成员都有一个角色&#xff0c;这决定了他们在项目中可以…

计算机网络-LDP标签发布与管理

前面学习了LDP建立邻居&#xff0c;建立会话&#xff0c;今天来学习在MPLS中的标签发布与管理。 在MPLS网络中&#xff0c;下游LSR决定标签和FEC的绑定关系&#xff0c;并将这种绑定关系发布给上游LSR。LDP通过发送标签请求和标签映射消息&#xff0c;在LDP对等体之间通告FEC和…