QCustomPlot实现曲线拖拽

本文内容目录

需求场景:

曲线图应该同时具有以下功能点:

1、在画布上进行鼠标框选,实现坐标缩放。

2、可以上下拖动某曲线。

一、选择控件

这里选择QCustomPlot,是一个C++绘图库,可以创建各种类型的绘图,包括散点图、曲线图、直方图、颜色地图、轮廓图等,可以跨平台使用。

以下是QCustomPlot的官方文档:
QCustomPlot官方网站

https://www.qcustomplot.com/

二、将QCustomPlot库整合到你的Qt项目中

1、下载源代码
2、创建.pri

.pri文件的内容添加如下:

cpp 复制代码
HEADERS += \
    $$PWD/qcustomplot.h

SOURCES += \
    $$PWD/qcustomplot.cpp

将.pri文件,.cpp文件,.h文件放到同一个文件夹下,示例项目为PlotTest。:

并在项目配置文件.pro文件中添加语句:

cpp 复制代码
include(CustomPlot/CustomPlot.pri)

重新构建后,可以看到qcustomplot库已经整合到项目中:

三、鼠标框选,实现坐标缩放

cpp 复制代码
实现代码:

mCustomPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
mCustomPlot->setSelectionRectMode(QCP::SelectionRectMode::srmZoom);

说明:

cpp 复制代码
mCustomPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);

setInteractions 函数配置图表的交互方式:

QCP::iRangeDrag:启用拖动操作,允许在图表上拖动以平移视图(画布的拖动而非拖动曲线)。
QCP::iRangeZoom:启用缩放操作,允许在图表上进行鼠标滚轮缩放或选择区域以进行缩放。
QCP::iSelectPlottables:启用可选择图表元素的操作。这允许你在图表上单击选择图形元素,以便进行进一步的操作或分析。

前两个选项用于框选,后一个选项支持曲线拖动。
cpp 复制代码
mCustomPlot->setSelectionRectMode(QCP::SelectionRectMode::srmZoom);

setSelectionRectMode 函数配置选择矩形的工作模式:

QCP::srmNone:禁用选择矩形,用户无法使用选择矩形进行任何操作。
QCP::srmZoom:启用选择矩形以进行缩放操作,用户可以在图表上创建选择矩形以放大或缩小选定区域。
QCP::srmSelect:启用选择矩形以选择图表中的对象,用户可以在图表上创建选择矩形以选择图表元素,例如曲线。
QCP::srmCustom:自定义选择矩形操作模式,可以根据需要自定义操作。

这里的矩形指的是框选矩形,如下所示:

四、曲线拖动

要实现曲线拖动的思路是,首先要定位,知道拖动的是哪条曲线,其次是当鼠标移动时,要根据鼠标坐标改变曲线。

1、定位曲线

通过信号selectionChangedByUser获得曲线index。

cpp 复制代码
connect(mCustomPlot, &QCustomPlot::selectionChangedByUser, this, &ResultCurves::getSelection);

在getSelection槽函数中,可以进行处理,可以用成员变量存储当前用户选中的曲线index
2、移动时改变曲线

重写鼠标移动事件mouseMoveEvent。

cpp 复制代码
void ResultCurves::mouseMoveEvent(QMouseEvent *event)
{
	//获取鼠标的本地窗口坐标
    double x = event->pos().x();
    double y = event->pos().y();
	//将鼠标指针的像素坐标转换为图表上的实际坐标
    double xCurve = mCustomPlot->xAxis->pixelToCoord(x);
    double yCurve = mCustomPlot->yAxis->pixelToCoord(y);
	//获取当前图表显示的y轴最大坐标,这里用于限制曲线的移动坐标范围
    double yMax = mCustomPlot->yAxis->range().upper;
    double yMin = mCustomPlot->yAxis->range().lower;

    mPosX = xCurve;
    mPosY = yCurve;

    if(mPosY > yMax){
        mPosY = yMax;
    }

    if(mPosY < yMin){
        mPosY = yMin;
    }
	//当目标曲线被选中时,在drawThresholdLine中进行曲线的重新绘制
    if(mIsThresholdSelected)
    {
        drawThresholdLine(mPosY);
    }
}

void ResultCurves::drawThresholdLine(double yValue)
{
	//在QCustomPlot对象和mGraphList曲线列表中删除目标曲线
    mCustomPlot->removeGraph(mGraphList.size() - 1);
    mGraphList.removeLast();
    //新增更新曲线
    QVector<double> temp;
    for(int i=0; i<mCurveList.begin().value().size(); i++)
    {
        temp.append(yValue);
    }

    addCurve(mGraphList.size(),mXValues,temp);
    mCustomPlot->replot();
}

五、问题的产生与解决

可以发现,以上两个功能的触发方式都是鼠标左键单击,经过实践,qcustomplot不能区分鼠标左键单击后是执行框选还是拖动,即使是在mousepressevent中判断是否选中目标曲线,若选中则mCustomPlot->setSelectionRectMode(QCP::SelectionRectMode::srmNone);(禁用矩形),也不能使两个功能共存。

1、查看源码

发现在QCustomPlot::mouseMoveEvent中,调用到一个函数processPointSelection:

该函数处理鼠标事件,特别是点选操作,也仅在QCustomPlot::mouseMoveEvent中调用。如果发生了选择状态的更改(selectionStateChanged 为 true),则发出 selectionChangedByUser 信号。

所以一个简单直接的思路是,当我想框选时,不调用该函数,不进行点选操作;当我想拖动时,调用该函数,发出 selectionChangedByUser 信号。

因此增加一个bool变量和接口:

2、修改本项目代码

在以上更改后,在我们自定义类的mousePressEvent中,当选中曲线index为目标曲线index时:

cpp 复制代码
mCustomPlot->setSelectionRectMode(QCP::SelectionRectMode::srmNone);
mCustomPlot->setHoverPicked(false);

而在mouseReleaseEvent中:

cpp 复制代码
mCustomPlot->setSelectionRectMode(QCP::SelectionRectMode::srmZoom);

从而最终可以满足文章开头的需求任务。

欢迎一起讨论!

相关推荐
白子寰8 分钟前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
小芒果_0113 分钟前
P11229 [CSP-J 2024] 小木棍
c++·算法·信息学奥赛
gkdpjj19 分钟前
C++优选算法十 哈希表
c++·算法·散列表
王俊山IT20 分钟前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
-Even-23 分钟前
【第六章】分支语句和逻辑运算符
c++·c++ primer plus
plmm烟酒僧1 小时前
Windows下QT调用MinGW编译的OpenCV
开发语言·windows·qt·opencv
我是谁??1 小时前
C/C++使用AddressSanitizer检测内存错误
c语言·c++
Black_Friend1 小时前
关于在VS中使用Qt不同版本报错的问题
开发语言·qt
CSUC1 小时前
【Qt】QTreeView 和 QStandardItemModel的关系
qt
发霉的闲鱼2 小时前
MFC 重写了listControl类(类名为A),并把双击事件的处理函数定义在A中,主窗口如何接收表格是否被双击
c++·mfc