本文内容目录
需求场景:
曲线图应该同时具有以下功能点:
1、在画布上进行鼠标框选,实现坐标缩放。
2、可以上下拖动某曲线。
一、选择控件
这里选择QCustomPlot,是一个C++绘图库,可以创建各种类型的绘图,包括散点图、曲线图、直方图、颜色地图、轮廓图等,可以跨平台使用。
以下是QCustomPlot的官方文档:
QCustomPlot官方网站
二、将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);
从而最终可以满足文章开头的需求任务。
欢迎一起讨论!