news 2026/10/30 1:14:51

QCustomPlot绘图实现光标滑过曲线显示点的坐标

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
QCustomPlot绘图实现光标滑过曲线显示点的坐标

有两种方法可实现这个效果:

1.通过关联QCustomPlot的mouseMove信号槽事件实现;

2.通过继承QCustomPlot类,重写mouseMove虚函数实现;

这个两个方法都是获取鼠标位置,然后计算出点的坐标值,利用QToolTip显示点坐标的方法。

方向弄清楚了,现在我们去实现出来,go……

一、信号槽方法

1、首先建立一个鼠标移动时间响应函数:

private slots: void MyMouseMoveEvent(QMouseEvent *event);

2、将槽函数MyMouseMoveEvent与QCustomPlot的mouseMove信号建立连接。
这里推荐基于qt5的信号槽连接,因为编译时,基于qt4的connect不会检查信号和槽的参数以及槽函数正确与否,而基于qt5的connect会有这一个检查过程(原来遇见过基于qt4的槽函数没有响应的情况)。

connect(m_pHistoryPlot, &QCustomPlot::mouseMove, this, &HistoryDataView::MyMouseMoveEvent);

3、槽函数实现光标划过曲线显示点坐标功能。
代码有详细注释,这里就不详细讲解。

void HistoryDataView::MyMouseMoveEvent(QMouseEvent* event) { if (m_pHistoryPlot->graphCount() == 0) { return; } //获取鼠标坐标,相对父窗体坐标 int x_pos = event->pos().x(); int y_pos = event->pos().y(); //鼠标坐标转化为CustomPlot内部坐标 float x_val = m_pHistoryPlot->xAxis->pixelToCoord(x_pos); float y_val = m_pHistoryPlot->yAxis->pixelToCoord(y_pos); //通过坐标轴范围判断光标是否在点附近 float x_begin = m_pHistoryPlot->xAxis->range().lower; float x_end = m_pHistoryPlot->xAxis->range().upper; float y_begin = m_pHistoryPlot->yAxis->range().lower; float y_end = m_pHistoryPlot->yAxis->range().upper; float x_tolerate = (x_end - x_begin) / 40;//光标与最近点距离在此范围内,便显示该最近点的值 float y_tolerate = (y_end - y_begin) / 40; //判断有没有超出坐标轴范围 if (x_val < x_begin || x_val > x_end) { return; } //通过x值查找离曲线最近的一个key值索引 int index = 0; int index_left = m_pHistoryPlot->graph(0)->findBegin(x_val, true);//左边最近的一个key值索引 int index_right = m_pHistoryPlot->graph(0)->findEnd(x_val, true);//右边 float dif_left = fabs(m_pHistoryPlot->graph(0)->data()->at(index_left)->key - x_val); float dif_right = fabs(m_pHistoryPlot->graph(0)->data()->at(index_right)->key - x_val); index = ((dif_left < dif_right) ? index_left : index_right); double x_posval = m_pHistoryPlot->graph(0)->data()->at(index)->key;//通过得到的索引获取key值 double y_posval = m_pHistoryPlot->graph(0)->data()->at(index)->value;//通过得到的索引获取value值 float dx = fabs(x_posval - x_val); float dy = fabs(y_posval - y_val); int graphIndex = 0;//curve index closest to the cursor //通过遍历每条曲线在index处的value值,得到离光标点最近的value及对应曲线索引 for (int i = 0, n = m_pHistoryPlot->xAxis->graphs().count(); i < n; i++) { y_posval = fabs(m_pHistoryPlot->graph(i)->data()->at(index)->value - y_val); if (y_posval < dy) { dy = y_posval; graphIndex = i; } } //判断光标点与最近点的距离是否在设定范围内 if (dy <= y_tolerate && dx <= x_tolerate) { y_posval = m_pHistoryPlot->graph(graphIndex)->data()->at(index)->value; QString strToolTip = QString("CH%1 \nx=%2\ny=%3").arg(graphIndex + 1).arg(y_posval).arg(y_posval); QToolTip::showText(cursor().pos(), strToolTip, m_pHistoryPlot); } }

4、最终效果

二、继承通过虚函数实现

1、继承QCustomPlot

class MyCustomPlot : public QCustomPlot ​​​​​​​{ Q_OBJECT public: MyCustomPlot(QWidget *parent = 0); protected: virtual void mouseMoveEvent(QMouseEvent *event); //…… }

2、重写虚函数

void MyCustomPlot::mouseMoveEvent(QMouseEvent *event) { QCustomPlot::mouseMoveEvent(event); if(!m_isShowTracer) { return; } //获取鼠标坐标,相对父窗体坐标 int x_pos = event->pos().x(); int y_pos = event->pos().y(); //鼠标坐标转化为CustomPlot内部坐标 float x_val = m_pHistoryPlot->xAxis->pixelToCoord(x_pos); float y_val = m_pHistoryPlot->yAxis->pixelToCoord(y_pos); //通过坐标轴范围判断光标是否在点附近 float x_begin = m_pHistoryPlot->xAxis->range().lower; float x_end = m_pHistoryPlot->xAxis->range().upper; float y_begin = m_pHistoryPlot->yAxis->range().lower; float y_end = m_pHistoryPlot->yAxis->range().upper; float x_tolerate = (x_end - x_begin) / 40;//光标与最近点距离在此范围内,便显示该最近点的值 float y_tolerate = (y_end - y_begin) / 40; //判断有没有超出坐标轴范围 if (x_val < x_begin || x_val > x_end) { return; } //通过x值查找离曲线最近的一个key值索引 int index = 0; int index_left = m_pHistoryPlot->graph(0)->findBegin(x_val, true);//左边最近的一个key值索引 int index_right = m_pHistoryPlot->graph(0)->findEnd(x_val, true);//右边 float dif_left = fabs(m_pHistoryPlot->graph(0)->data()->at(index_left)->key - x_val); float dif_right = fabs(m_pHistoryPlot->graph(0)->data()->at(index_right)->key - x_val); index = ((dif_left < dif_right) ? index_left : index_right); double x_posval = m_pHistoryPlot->graph(0)->data()->at(index)->key;//通过得到的索引获取key值 double y_posval = m_pHistoryPlot->graph(0)->data()->at(index)->value;//通过得到的索引获取value值 float dx = fabs(x_posval - x_val); float dy = fabs(y_posval - y_val); int graphIndex = 0;//curve index closest to the cursor //通过遍历每条曲线在index处的value值,得到离光标点最近的value及对应曲线索引 for (int i = 0, n = m_pHistoryPlot->xAxis->graphs().count(); i < n; i++) { y_posval = fabs(m_pHistoryPlot->graph(i)->data()->at(index)->value - y_val); if (y_posval < dy) { dy = y_posval; graphIndex = i; } } //判断光标点与最近点的距离是否在设定范围内 if (dy <= y_tolerate && dx <= x_tolerate) { y_posval = m_pHistoryPlot->graph(graphIndex)->data()->at(index)->value; //QString strToolTip = QString("CH%1:(%2, %3)").arg(graphIndex + 1).arg(x_val).arg(y_val); int hor = x_posval / 3600; int tmp = (int)x_posval % 3600; int min = tmp / 60; int sec = tmp % 60; char arx[32] = { 0 }; sprintf(arx, "%02d:%02d:%02d", hor, min, sec); QString strToolTip = QString("CH%1 \nx=%2\ny=%3").arg(graphIndex + 1).arg(arx).arg(y_posval); QToolTip::showText(cursor().pos(), strToolTip, m_pHistoryPlot); } this->replot();//曲线重绘 }

QT5下,使用QCustomPlot显示折线图和曲线图,鼠标滑过折线曲线跟随鼠标显示此时鼠标指向的点的x轴数值和y轴数值。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 1:13:18

Spring Boot 3.2 整合 Elasticsearch 8.3 与 RabbitMQ 实战:版本对齐与数据同步

简介&#xff1a;这份资源面向具备一定 Spring Boot 基础、希望掌握实时搜索与数据同步方案的后端开发者&#xff0c;围绕 Spring Boot 整合 Elasticsearch 8.3 并通过 RabbitMQ 同步 MySQL 数据展开&#xff0c;覆盖依赖配置、Repository 操作、消息监听与发送、事务事件监听、…

作者头像 李华
网站建设 2026/10/30 1:12:44

J-Link、ST-Link与DAPLink调试器对比:从SWD协议到工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/30 1:12:40

泰科174357-2汽车端子拆解:MCP系列结构、压接与失效分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/30 1:12:40

STM32实战真相:从上电启动到外设坑点的硬件级解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/30 1:12:17

基于RK3588与NPU的仿生人头:从机械结构到AI交互全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/30 1:12:14

西门子PLC编程语言选型指南:LAD、FBD、STL实战对比与切换技巧

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华