QCustomPlot系列(7)-鼠标跟随显示坐标值(也即:十字游标功能)游标可以吸附采样点

博客介绍了QCustomPlot中实现游标实时动态功能的效果,如十字线游标X值跟随鼠标、Y值跟随曲线等。还探讨了插入游标、绿色文本框相关问题,包括游标跟随鼠标X值、文本框跟随游标、禁止游标出现在无点处等,以及多曲线时获取游标对应Y值的实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先来个动图看看效果:

这个动图中实现的功能有:1、十字线游标的X值跟随鼠标,Y值不跟随。2、游标的Y值跟随鼠标的X值对应的曲线的Y值,可以点击图例,更换游标跟随哪个曲线的Y值,3、游标不会出现在曲线中不存在点上,而是自动吸附到距离鼠标最近的曲线上的点。

在QCustomPlot插入游标、绿色文本框,都很简单,可参考我的另一篇博客《QCustomPlot系列(6)-》。

现在的问题有这些:

1、如何让插入的游标只跟随鼠标的X值,不跟随Y值?

2、如何让绿色文本框跟着游标动?

3、如何禁止游标出现在曲线中不存在点上?

在此之前先设置一下游标样式:
 

tracer->setPen(QPen(Qt::DashLine));//游标线型:虚线
tracer->setStyle(QCPItemTracer::tsPlus);//游标样式:十字星、圆圈、方框等

游标跟随鼠标的X值移动,只需覆盖QCustomplot的鼠标移动事件就好了:

void MultiCurvesPlot::mouseMoveEvent(QMouseEvent *event)
{
    QCustomPlot::mouseMoveEvent(event);//避免让父类的移动事件失效

    if(tracerEnable)//游标使能
    {
        if(getGraphs().contains(traceGraph))//检查一下游标附着的graph是否还存在
        {
            double x = xAxis->pixelToCoord(event->pos().x());//鼠标点的像素坐标转plot坐标
            tracer->setGraph(traceGraph);//设置游标吸附在traceGraph这条曲线上
            tracer->setGraphKey(x);//设置游标的X值(这就是游标随动的关键代码)
            double traceX = tracer->position->key();
            double traceY = tracer->position->value();

            tracerXText->setText(QDateTime::fromMSecsSinceEpoch(traceX * 1000.0).toString("hh:mm:ss.zzz"));//游标文本框,指示游标的X值
            tracerYText->setText(QString::number(traceY));//游标文本框,指示游标的Y值

            //计算游标X值对应的所有曲线的Y值
            for(int i = 0; i < graphCount(); i++)
            {
                //搜索左边离traceX最近的key对应的点,详情参考findBegin函数的帮助
                QCPDataContainer<QCPGraphData>::const_iterator coorPoint = graph(i)->data().data()->findBegin(traceX, , true);//true代表向左搜索
                qDebug() << QString("graph%1对应的Y值是").arg(i) << coorPoint->value;

            }
        }
    }
}

上面有几个函数是我自己写的,不是QCustomplot自带的,稍微解释一下上述代码:
getGraphs()是获取当前plot中所有的graph列表,实际上QCustomplot类中有个private变量在维护这个QList,这个函数把这个List给传出来了。
traceGraph是记录此刻游标与哪个graph关联。点击图例中的曲线名称,可以修改traceGraph。

还有一个小问题就是,当鼠标点的位置并没有曲线中的X与之完全相等时,要把游标强制显示到采样点出处,如下图所示。

由下图可见,游标并没有完全跟随鼠标的X值,而是被采样点给吸过去了。

实现这一功能也很简单,这是QCustomplot自带的功能,只需在游标初始化时,设置:
tracer->setInterpolating(false);//游标禁用插值
一旦设置了禁用插值属性,当设置游标位置时(也即tracer->setGraphKey(x);),setGraphKey(x)函数所设置的标志位会导致游标的位置更新函数QCPItemTracer::updatePosition()中会自动搜索一个距离形参x最近的采样点的x,作为游标的x值,这样就实现了“采样点捕捉游标”的功能,也即:游标的X值总是永远等于某个采样点的X值!

最后一个问题,当plot中有多条曲线(graph)时,我们希望能够看到当前游标X对应的所有的曲线的Y值,这个功能该怎么写?

原理步骤是这样的:

(1)拿到被游标处理过(被采样点捕捉后的游标)的横轴坐标double traceX。
(2)遍历曲线(graph),利用graph(i)->data().data()->findBegin(traceX, true)来计算graph(i)曲线中距离traceX最近的采样点,
findBegin的第二个参数为true,true代表搜出的最近的采样点在traceX的左边,false代表在右边。

<think>好的,我需要帮助用户了解如何使用QTQCustomPlot库实现动态曲线图的各种交互功能。首先,我得整理用户提到的需求:纵轴设置(颜色、数量)、横轴共享、缩放、自适应范围、游标显示坐标、视图重置、光标功能、差值计算以及缓存点数量配置。接下来,我应该参考提供的引用内容,看看是否有相关的实现方法。 根据引用[1],QCustomPlot的结构包括坐标轴矩形QCPAxisRect,可以添加多个坐标轴。这可能涉及到如何创建和配置纵轴的颜色和数量。引用[2]提到游标功能,特别是如何设置吸附采样点游标,这可能对显示坐标有帮助。引用[3]提到动态图的实现和性能优化,可能涉及到数据缓存和更新机制。 首先,纵轴设置颜色和数量可能需要使用QCPAxisRect来管理多个轴,每个轴可以单独设置颜色。横轴共享可能意味着多个曲线共享同一个横轴,这需要在添加新图层时使用相同的横轴实例。缩放和自适应范围可能需要调整坐标轴的范围自动调整策略,可能使用rescaleAxes()函数。游标显示坐标可以参考引用[2]中的QCPItemTracer设置,禁用插值以实现吸附效果。视图重置可能需要连接按钮信号到缩放重置函数。光标功能可能涉及跟踪鼠标位置并更新游标位置。差值计算可能需要插值方法,但用户可能希望禁用插值以显示实际数据点,如引用[2]所述。缓存点数量配置可能涉及限制显示的数据点数量,使用数据容器的squeeze()方法或设置固定容量。 接下来,我需要将这些功能整合到一个示例代码中,确保每个部分都正确配置。例如,初始化多个纵轴,设置它们的颜色,确保横轴共享,配置缩放和拖动交互,添加游标并连接鼠标移动事件,设置自适应范围,以及实现数据更新时的缓存管理。 在代码示例中,需要注意正确包含QCustomPlot的头文件和库,以及在pro文件中添加必要的模块如printsupport。动态更新数据可能需要使用定时器来模拟实时数据添加,并在每次更新时调整横轴范围,保持最新的数据可见。 此外,需要处理用户交互,比如鼠标移动时更新游标位置,并显示坐标值。这里可能需要使用QCPItemTracer和文本标签来实时显示坐标。在数据量大的情况下,合理配置缓存点数量,避免内存问题,同时保持流畅的显示效果。 最后,生成相关问题,帮助用户进一步学习,比如性能优化、多轴同步方法等。需要确保问题覆盖用户可能感兴趣的进阶主题,并引用相关参考资料中的内容,如引用[3]提到的处理大数据量时的性能优势。</think>以下是如何使用QCustomPlot实现动态曲线图的详细步骤和示例代码: ### 一、基础配置 1. **添加库文件** 将`qcustomplot.h`和`qcustomplot.cpp`加入工程,pro文件中添加: ```pro QT += printsupport core gui ``` ### 二、动态曲线核心功能实现 ```cpp // 初始化图表 QCustomPlot *customPlot = new QCustomPlot(this); QCPAxisRect *axisRect = customPlot->axisRect(); // 1. 纵轴设置(颜色、数量) QCPAxis *yAxis1 = customPlot->yAxis; // 默认左纵轴 yAxis1->setLabelColor(Qt::blue); // 设置颜色 QCPAxis *yAxis2 = new QCPAxis(axisRect, QCPAxis::atRight); // 添加右侧纵轴 yAxis2->setLabelColor(Qt::red); // 2. 横轴共享 QCPAxis *xAxis = customPlot->xAxis; // 所有曲线共享此横轴 // 3. 添加曲线 QCPGraph *graph1 = customPlot->addGraph(xAxis, yAxis1); QCPGraph *graph2 = customPlot->addGraph(xAxis, yAxis2); ``` ### 三、交互功能配置 ```cpp // 4. 缩放与平移 customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); // 5. 自适应范围(自动调整坐标轴范围) connect(customPlot, &QCustomPlot::mouseDoubleClick, [=](){ customPlot->rescaleAxes(); customPlot->replot(); }); // 6. 游标与坐标显示吸附数据点) QCPItemTracer *tracer = new QCPItemTracer(customPlot); tracer->setInterpolating(false); // 禁用插值[^2] tracer->setGraph(graph1); // 7. 实时显示坐标 customPlot->setMouseTracking(true); connect(customPlot, &QCustomPlot::mouseMove, [=](QMouseEvent *event){ double x = customPlot->xAxis->pixelToCoord(event->pos().x()); tracer->setGraphKey(x); // 自动吸附最近数据点 QPointF point = tracer->position->coords(); customPlot->setToolTip(QString("X:%1\nY:%2").arg(point.x()).arg(point.y())); }); ``` ### 四、动态更新与缓存控制 ```cpp // 8. 数据更新(示例:定时器模拟实时数据) QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, [=](){ static double t = 0; graph1->addData(t, qSin(t)); graph2->addData(t, qCos(t)); t += 0.1; // 9. 缓存点数量控制(保留最近500个点) if(graph1->data()->size() > 500) graph1->data()->removeBefore(t-10); }); timer->start(50); // 10. 横轴自动滚动 connect(timer, &QTimer::timeout, [=](){ xAxis->setRange(t+1, 8, Qt::AlignRight); // 保持显示最新8秒数据 customPlot->replot(); }); ``` ### 五、关键功能对比表 | 功能 | 实现方法 | |------------------|--------------------------------------------------------------------------| | 多纵轴颜色 | 通过`QCPAxis::setLabelColor()`设置 | | 数据点吸附 | `tracer->setInterpolating(false)`禁用插值 | | 性能优化 | 使用`data()->removeBefore()`控制缓存数量 | | 坐标轴同步 | 多个graph共享相同的xAxis实例 | | 实时更新效率 | 10万数据量下仍流畅[^3] | ### 六、视图重置功能扩展 ```cpp // 添加重置按钮 QPushButton *btnReset = new QPushButton("Reset View", this); connect(btnReset, &QPushButton::clicked, [=](){ customPlot->rescaleAxes(); customPlot->replot(); }); ```
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值