图 --->图

(graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。

线性表中我们把数据元素叫做元素,树中叫结点,在图中数据元素我们称之为顶点(Vertex)。

线性表可以没有数据元素,称为空表,树中可以没有结点,叫做空树,而图结构在咱国内大部分的教材中强调顶点集合V要有穷非空。

线性表中相邻的数据元素之间具有线性关系,树结构中,相邻两层的结点具有层次关系,而图结构中,任意两个顶点之间都有可能有关系,顶点之间的逻辑关系用边来表示,边集可以是空的。

无向边:若顶点Vi到Vj之间的边没有方向,则称这条边为无向边(Edge),用无序偶(Vi,Vj)来表示。


上图G1是一个无向图,G1={V1,E1},其中

-V1={A,B,C,D},

-E1={(A,B),(B,C),(C,D),(D,A),(A,C)}

有向边:若从顶点Vi到Vj的边有方向,则称这条边为有向边,也称为弧(Arc),用有序偶<Vi,Vj>来表示,Vi称为弧尾,Vj称为弧头。


上图G2是一个无向图,G2={V2,E2},其中

-V2={A,B,C,D},

-E2={<B,A>,<B,C>,<C,A>,<A,D>}

简单图:在图结构中,若不存在顶点到其自身的边,且同一条边不重复出现,则称这样的图为简单图。

无向完全图,在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图。含有N个顶点的无向完全图有n*(n-1)/2条边。


有向完全图:在有向图中,如果任意两个顶点之间都存在方向护卫相反的两条弧,则称该图为有向完全图,含有n个顶点的有向完全图有n*(n-1)条边。


稀疏图和稠密图:相对而言,通常认为边或弧数小于n*logn(n是顶点个数)的图称为稀疏图,反之为稠密图。

有些图的边或弧带有与它相关的数字,这种与图的边或弧相关的数叫做权(weight),带权的图通常称为网。

子图的概念:


图的顶点与边之间的关系

对于无向图G=(V,G),如果边(V1,V2)属于E,则称顶点V1和V2互为邻接点,即V1和V2相连接。变(V1,V2)依附于顶点V1和V2,或者说边(V1,V2)

与顶点V1和V2相关联。

顶点V的度适合V相关联的边的数目,记为TD(V)如下图,顶点A和B互为邻接点,边(A,B)依附于顶点A与B上,顶点A的度为3.


对于有向图G=(V,E),如果有<V1,V2>属于E,则称顶点V1邻接到顶点V2,顶点V2链接自顶点V1.

以顶点V为头的弧的数目称为V的入度,记为ID(V),以V为尾的数目称为V的出度。记为OD(V),因此顶点V的度为TD(V)=ID(V)+OD(V).

下图顶点A的入度为2,出度是1,所以顶点A的度是3.


无向图G=(V,E)中从顶点V1到顶点V2的路径。

下图用红线列举了从顶点B到顶点D的四种不同路径:


如果G是有向图,则路径也是有向的。

修用红线列举顶点B到顶点D的两种路径,而顶点A到顶点B就不存在路径啦:


路径的长度是路径上的边或弧的数目,

第一个顶点到最后一个顶点相同的路径称为回路或环

序列中顶点不重复出现的路径称为简单路径,除了第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路,称为简单回路或简答环。下图左侧是简单环,右侧不是简单环。


连通图

在无向图G中,如果从顶点V1到顶点V2有路径,则称V1和V2是连通的,如果对于途中任意两个顶点Vi和Vj都是连通的,则称G是连通图。下图左侧不是连通图,右侧是连通图。


无向图中的极大连通子图称为连通分量。

注意以下概念:

-首先要是子图,并且子图是要连通的。

-连通子图含有极大顶点数。

-具有极大顶点数的连通子图包含依附于这些顶点的所有边。


在有向图G中,如果对于每一对Vi到Vj都存在路径,则称G是强连通图

有向图中的极大强连通子图称为有向图的强连通分量。

下图左侧并不是强连通图,右侧是。并且右侧是左侧的极大强连通子图,也是左侧的强连通分量。


最后我们再来看连通图的生成树定义

所谓的一个连通图的生成树是一个极小的连通子图,它含有图中全部的n个顶点,但只有足以构成一棵树的n-1条边。

如果一个有向图恰有一个顶点入度为0,其余顶点的入度为1,则是一棵有向树。


图的存储结构

图的存储结构相比较线性表与树来说就复杂很多。我们回顾与Ixia,对于线性表来说,是一对一的关系, 所以用数组或者链表均可简单存放。树结构是一对多的关系,所以我们要将数组和链表的特性结合在一起才能更好的存放,。

那么我们的图,是多对多的情况,另外图上的任何一个顶点都可以被看做是第一个顶点,任一顶点的邻接点之间也不存在次序关系。

因为任意两个顶点之间都可能存在联系,因此无法以数据元素在内存中的物理位置来表示元素之间的关系(内存物理位置是线性的,图的元素关系是平面的)。

如果用多重链表来描述是可以做到,但在几节课前的树章节我们已经讨论过,纯粹用多重链表导致的浪费是无法想象的(如果各个顶点的度数相差太大,就会造成巨大的浪费)。

所幸,前辈们已经帮我们想好了出路,我们接下来会谈吐的五种不同的存储结构:

邻接矩阵(无向图)

考虑到图是由顶点和边或弧两部分组成,合在一起比较困难,那就是很自然地考虑到分为两个结构来分别存储。

顶点因为不区分大小,主次,所以用一个一维数组来存储是很不错的选择。而边或弧由于是顶点和顶点之间的关系,一维数组就肯定搞不定了,那我们不妨考虑用一个二维数组来存储。

图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。


我们可以设置两个数组,顶点数组为verTex[4] = {V0,V1,V2,V3};边数组arc[4][4]为对称矩阵(0表示不存在顶点间的边,1表示顶点间存在边)

对称矩阵:所谓对称矩阵就是n阶矩阵的元满足a[i][j] =a[j][i](0<=I,j<=n)。即从矩阵的左上角到右下角的主对角线为轴,右上角的元与左下角相对应的元都是相等的。

有了这个二维数组组成的对称矩阵,我们就可以很容易地知道图中的信息:

-要判定任意两顶点是否有边无边就非常容易了;

-要知道某个顶点度,其实就是这个顶点Vi在邻接矩阵中第i行(或第i列)的元素之和。

-求顶点Vi的所有邻接点就是将矩阵中第i行元素扫描一遍,arc[i][j]为1就是邻接点喽。

邻接矩阵(有向图)

无向图的边构成了一个对称矩阵,貌似浪费了一半的空间,那如果是有向图来存放,会不会把资源都利用得很好呢?


可见顶点数组vertex[4]={V0,V1,V2,V3},弧数组arc[4][4]也是一个矩阵,但以为是有向图,所以这个矩阵并不对称,例如由V1到V0有弧,得到arc[1][0]=1,而V0到V1没有弧,因此arc[0][1]=0;

另外有向图是有讲究的,要考虑入度和出度,顶点V1的入度为1,正好是滴V1列的各数之和,顶点V1的出度为2,正好是第V2行的各数之和。

邻接矩阵(网)

在图的术语中,我们提到了网这个概念,事实上也就是每条边上带有权的图就叫网。


这里∞表示一个计算机允许的,大于所有边上权值的值。

邻接表(无向图)

邻接矩阵看上去是个不错的选择,首先是容易理解,第二是索引和编排都很舒服,但是我们也发现,对于边数相对顶点较少的图,这种结构无疑是存在对存储空间的极大浪费。


因此我们可以考虑另外一种存储结构方式,例如把数组和链表结合一起来存储,这种方式在图结构也适用,我们称为邻接表。

邻接表的处理方法是:

-图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过数组可以较容易地读取顶点信息,更加方便。

-图中每个 顶点Vi的所有邻接点构成一个线性表,由于邻接点的个数不确定,所以我们选择用单链表来存储。


若是有向图,邻接表结构也是类似的,我们先来看下把顶点当弧尾简历的邻接表,这样很容易就可以得到每个顶点的出度:


邻接表有个缺陷就是要么表示出度要么表示入度,只能一种,上图是以出度为例,正向邻接表,入度是逆邻接表。

单有事为了便于确定顶点的入度或以顶点为弧头的弧,我们可以建立一个有向图的逆邻接表。


此时我们很容易就可以算出某个顶点的入度或出度是多少,判断两顶点是否存在弧也很容易实现。

邻接表(网)

对于带权值的网图,可以再边表结点定义中再增加一个数据域来存储权值即可:


十字链表

邻接表固然优秀,但也有不足,例如对有向图的处理上,有时候需要再建立一个逆邻接表~那我们思考:有没有可能把邻接表和逆邻接表结合起来呢?

答案是肯定的,这就是我们现在要谈的十字链表,为此我们重新定义顶点表结点结构:


data代表顶点数据,firstIn代表入边表的指针,firstOut代表出边表的指针。

j接着重新定义边表结点结构:


tailVex表示的是弧起点的顶点下标,headVex代表的是弧的终点的顶点下标,上图表示的是一条边,而不是一个点。

边有向的话称为弧。


图中表示的是十字链表的结构,蓝色的表示是左边顶点的出度(邻接表表示出度),那么红色呢代表的是入度(逆邻接表)。十字链表就是扩充了边表的结构,使得逆邻接表有位置填充进去。

十字链表的好处就是因为把邻接表和逆邻接表整合在了一起,这样即容易找到以Vi为尾的弧,也容易找到一Vi为偷的弧,因而容易求得顶点的出度和入度。

十字链表除了结构复杂一点外,其实创建图算法的时间复杂度是和邻接表相同的,因此,在有向图的应用中,十字链表也是非常好的数据结构模型。

如果程序只关心出度那建立 一个邻接表就行了,如果只考虑入度建立一个逆邻接表就行了。

链接多重表

如果我们在无向图的应用中, 关注的终点是顶点的话,那么邻接表是不错的选择, 但如果我们更关注的是边的操作,比如对已经访问过的边做标记,或者删除某一条边等操作邻接表就显得不那么方便了。

例如下图,若要删除(V0,V2)这条边,就需要对邻接表结构中边表的两个结点进行删除操作。

 


因此,我们也仿照十字链表的方式,对边表结构进行改装,重新定义的边表结构如下:


其中iVex和jVex是与某条边依附的两个顶点在顶点表中的下标。iLink指向依附一丁点iVex的下一条边,jLink指向依附顶点jVex的下一条边。

也就是说在邻接多重表里边,边表存放的是一条边,而不是一个顶点。


边集数组

边集数组是由两个一维数组构成,一个是存储顶点的信息,;另一个是存储边的信息,这个边数组每个数据元素是由一条边的七起点下标(begin),终点下标(end),和权(weight)组成。


图的遍历

树的遍历我们谈了四种方式,大家回忆一下,树因为根节点只有一个吗,并且所有的结点都只有一个双亲,所以不是很难理解。但是谈到图的遍历,那么,饿复杂多了,因为它的任一顶点都可以和其他的所有顶点相连接,因此极有可能存在重复走过某个顶点或漏了某个顶点的遍历过程。

对于图的遍历,如果要避免以上情况,那就需要科学地设计遍历方案,通常有两种遍历次序方案,我们是深度优先遍历和广度右边遍历。

深度优先遍历(DepthFirstSearch),也称为深度优化搜索,简称为DFS。

我们可以约定y右手原则,在没有碰到重复顶点的情况下,分叉路口始终是向右手边走,每路过一个顶点就做一个记号。如果发现右边边已经走过,就走其他方向,如果全部走过,就退回上一步,再次判断有没有路口可以走,有就继续按照刚才的四路走下去,如果没有再退回去。最终再次回到起点,全部遍历完毕。这其实就是一个递归。

广度优先遍历(BreadthFirstSearch),又称为深度优先搜索,简称为BFS。


深度优先遍历类似于树的遍历前序遍历,例如上图的左侧,广度优先遍历类似于树的层序遍历,一层一层的逐步往下,我们可以用队列和右手原则来实现广度优先遍历。。



void ImageView::updateCurShowImage() { auto curRegionName = ui->comboBox_regions->currentText(); bool isDetect = ui->radioButton_imageDetect->isChecked(); if (ui->radioButton_workIndex->isChecked()) { //作业序模式 //只做显示 ui->comboBox_tasks->setEnabled(false); ui->stackedWidget->setCurrentWidget(ui->page_2); ui->stackedWidget_taskName->setCurrentWidget(ui->page_4); const auto imageCount = this->getImageCountWithWorkIndex(curRegionName, isDetect); ui->label_workIndex->setText(QString("/") + QString::number(imageCount)); if (imageCount <= 0) { //没有像可以显示 ui->lineEdit_imageWorkIndex->setText(QString::number(0)); } auto curIndexText = ui->lineEdit_imageWorkIndex->text(); bool convertIndexResult = false; auto curIndex = curIndexText.toInt(&convertIndexResult); ui->widget_view->clearImage(); if (convertIndexResult) { if (curIndex == 0 && imageCount > 0) { //直接从1开始 ui->lineEdit_imageWorkIndex->setText(QString::number(1)); curIndex = 1; } curIndex -= 1; auto getImageInfoResult = this->getImageInfo(curRegionName, isDetect, curIndex); if (getImageInfoResult.has_value()) { auto curInfo = getImageInfoResult.value(); this->setDisplayImageByPath(curInfo.m_imageFilePath); //显示任务名字 ui->lineEdit_taskName->setText(curInfo.m_taskName); } auto curBatchIndex = this->getImageInfoBatchIndex(curRegionName,isDetect, curIndex); if (curBatchIndex < 0) { ui->label_batchIndex->setText({}); } else { ui->label_batchIndex->setText(tr("%1 批").arg(curBatchIndex + 1)); } } else { //序号无效 ui->label_batchIndex->setText({}); ui->lineEdit_taskName->setText({}); } return; } ui->stackedWidget->setCurrentWidget(ui->page_1); ui->stackedWidget_taskName->setCurrentWidget(ui->page_3); ui->comboBox_tasks->setEnabled(true); this->onImageNumLineEditTextChanged(ui->lineEdit_image_num->text()); }、
03-13
修改了之后还是不行,麻烦再帮我看一下。 QSharedPointer<QCPAxisTickerFixed> xTicker1(new QCPAxisTickerFixed); QSharedPointer<QCPAxisTickerFixed> xTicker2(new QCPAxisTickerFixed); QSharedPointer<QCPAxisTickerFixed> xTicker3(new QCPAxisTickerFixed); QSharedPointer<QCPAxisTickerFixed> xTicker4(new QCPAxisTickerFixed); xTicker1->setTickStep(1.0); // 设置步长 xTicker1->setScaleStrategy(QCPAxisTickerFixed::ssNone); // 禁用自动缩放 xTicker2->setTickStep(1.0); // 设置步长 xTicker2->setScaleStrategy(QCPAxisTickerFixed::ssNone); // 禁用自动缩放 xTicker3->setTickStep(1.0); // 设置步长 xTicker3->setScaleStrategy(QCPAxisTickerFixed::ssNone); // 禁用自动缩放 xTicker4->setTickStep(1.0); // 设置步长 xTicker4->setScaleStrategy(QCPAxisTickerFixed::ssNone); // 禁用自动缩放 ui->widget_1->xAxis->setTicker(xTicker1); ui->widget_1->xAxis->setRange(0,20);//设定x轴的范围 ui->widget_2->xAxis->setTicker(xTicker2); ui->widget_2->xAxis->setRange(0,20);//设定x轴的范围 ui->widget_3->xAxis->setTicker(xTicker3); ui->widget_3->xAxis->setRange(0,20);//设定x轴的范围 ui->widget_4->xAxis->setTicker(xTicker3); ui->widget_4->xAxis->setRange(0,20);//设定x轴的范围 ui->widget_1->yAxis->setLabel(QStringLiteral("温度t"));//设置y坐标轴名称 ui->widget_1->yAxis->setLabelColor(QColor(20,20,20));//设置y坐标轴名称颜色 ui->widget_2->yAxis->setLabel(QStringLiteral("光功率"));//设置y坐标轴名称 ui->widget_2->yAxis->setLabelColor(QColor(20,20,20));//设置y坐标轴名称颜色 ui->widget_3->yAxis->setLabel(QStringLiteral("浓度"));//设置y坐标轴名称 ui->widget_3->yAxis->setLabelColor(QColor(20,20,20));//设置y坐标轴名称颜色 ui->widget_4->yAxis->setLabel(QStringLiteral("温度T"));//设置y坐标轴名称 ui->widget_4->yAxis->setLabelColor(QColor(20,20,20));//设置y坐标轴名称颜色 ui->widget_1->axisRect()->setupFullAxesBox(true);//设置缩放,拖拽,设置表的分类标显示位置 ui->widget_1->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom| QCP::iSelectAxes); //允许表交互 ui->widget_1->axisRect()->insetLayout()->setInsetAlignment(0,Qt::AlignTop | Qt::AlignRight);//例显示位置右上 ui->widget_1->legend->setVisible(true);//显示例 ui->widget_2->axisRect()->setupFullAxesBox(true);//设置缩放,拖拽,设置表的分类标显示位置 ui->widget_2->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom| QCP::iSelectAxes); ui->widget_2->axisRect()->insetLayout()->setInsetAlignment(0,Qt::AlignTop | Qt::AlignRight);//例显示位置右上 ui->widget_2->legend->setVisible(true);//显示例 ui->widget_3->axisRect()->setupFullAxesBox(true);//设置缩放,拖拽,设置表的分类标显示位置 ui->widget_3->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom| QCP::iSelectAxes); ui->widget_3->axisRect()->insetLayout()->setInsetAlignment(0,Qt::AlignTop | Qt::AlignRight);//例显示位置右上 ui->widget_3->legend->setVisible(true);//显示例 ui->widget_4->axisRect()->setupFullAxesBox(true);//设置缩放,拖拽,设置表的分类标显示位置 ui->widget_4->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom| QCP::iSelectAxes); ui->widget_4->axisRect()->insetLayout()->setInsetAlignment(0,Qt::AlignTop | Qt::AlignRight);//例显示位置右上 ui->widget_4->legend->setVisible(true);//显示
03-08
#include "mainwindow.h" #include "ui_Mainwindow.h" #include "qcustomplot.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); // 创建主工具栏 mainToolBar = new QToolBar("主工具栏", this); mainToolBar->setMovable(false); // 禁止工具栏移动 addToolBar(Qt::TopToolBarArea, mainToolBar); ui->customPlot->addGraph(); ui->customPlot->graph(0)->setPen(QPen(Qt::blue)); // 线条颜色为蓝色 // 生成一些数据点 QVector<double> x(251), y0(251); for (int i=0; i<251; ++i) { x[i] = i; y0[i] = qExp(-i/150.0)*qCos(i/10.0); // 指数衰减的余弦函数 } // 配置右轴和上轴显示刻度但不显示标签 ui->customPlot->xAxis2->setVisible(true); ui->customPlot->xAxis2->setTickLabels(false); ui->customPlot->yAxis2->setVisible(true); ui->customPlot->yAxis2->setTickLabels(false); // 使左轴和下轴的范围变化同步到右轴和上轴 connect(ui->customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), ui->customPlot->xAxis2, SLOT(setRange(QCPRange))); connect(ui->customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), ui->customPlot->yAxis2, SLOT(setRange(QCPRange))); // 将数据点传递给形 ui->customPlot->graph(0)->setData(x, y0); // 自动调整坐标轴范围以适应形 ui->customPlot->graph(0)->rescaleAxes(); // 允许用户通过鼠标拖动坐标轴范围、使用鼠标滚轮缩放以及通过点击选择形 ui->customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); // 计算表中心位置 double xCenter = (ui->customPlot->xAxis->range().lower + ui->customPlot->xAxis->range().upper) / 2; double yCenter = (ui->customPlot->yAxis->range().lower + ui->customPlot->yAxis->range().upper) / 2; // 初始化第一组纵向和横向光标线 verticalLine1 = new QCPItemStraightLine(ui->customPlot); verticalLine1->setPen(QPen(Qt::red)); verticalLine1->point1->setCoords(0, ui->customPlot->yAxis->range().lower); verticalLine1->point2->setCoords(0, ui->customPlot->yAxis->range().upper); verticalLine1->setVisible(false); horizontalLine1 = new QCPItemStraightLine(ui->customPlot); horizontalLine1->setPen(QPen(Qt::red)); horizontalLine1->point1->setCoords(ui->customPlot->xAxis->range().lower, 0); horizontalLine1->point2->setCoords(ui->customPlot->xAxis->range().upper, 0); horizontalLine1->setVisible(false); // 初始化第一组标签 verticalLabel1 = new QCPItemText(ui->customPlot); verticalLabel1->setLayer("overlay"); // 确保标签显示在最上层 verticalLabel1->setClipToAxisRect(false); verticalLabel1->setPositionAlignment(Qt::AlignBottom|Qt::AlignHCenter); verticalLabel1->position->setType(QCPItemPosition::ptAxisRectRatio); verticalLabel1->position->setCoords(0, 0); // 初始位置 verticalLabel1->setText("Time: 0"); verticalLabel1->setFont(QFont(font().family(), 9)); verticalLabel1->setPen(QPen(Qt::red)); verticalLabel1->setBrush(QBrush(Qt::white)); verticalLabel1->setVisible(false); horizontalLabel1 = new QCPItemText(ui->customPlot); horizontalLabel1->setLayer("overlay"); horizontalLabel1->setClipToAxisRect(false); horizontalLabel1->setPositionAlignment(Qt::AlignLeft|Qt::AlignVCenter); horizontalLabel1->position->setType(QCPItemPosition::ptAxisRectRatio); horizontalLabel1->position->setCoords(0, 0); // 初始位置 horizontalLabel1->setText("Voltage: 0"); horizontalLabel1->setFont(QFont(font().family(), 9)); horizontalLabel1->setPen(QPen(Qt::red)); horizontalLabel1->setBrush(QBrush(Qt::white)); horizontalLabel1->setVisible(false); // 初始化第二组纵向和横向光标线 verticalLine2 = new QCPItemStraightLine(ui->customPlot); verticalLine2->setPen(QPen(Qt::green)); // verticalLine2->point1->setCoords(0, ui->customPlot->yAxis->range().lower); // verticalLine2->point2->setCoords(0, ui->customPlot->yAxis->range().upper); verticalLine2->point1->setCoords(xCenter + 10, ui->customPlot->yAxis->range().lower); verticalLine2->point2->setCoords(xCenter + 10, ui->customPlot->yAxis->range().upper); verticalLine2->setVisible(false); horizontalLine2 = new QCPItemStraightLine(ui->customPlot); horizontalLine2->setPen(QPen(Qt::green)); // horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, 0); // horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, 0); horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, yCenter + 0.2); horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, yCenter + 0.2); horizontalLine2->setVisible(false); // 初始化第二组标签 verticalLabel2 = new QCPItemText(ui->customPlot); verticalLabel2->setLayer("overlay"); // 确保标签显示在最上层 verticalLabel2->setClipToAxisRect(false); verticalLabel2->setPositionAlignment(Qt::AlignBottom|Qt::AlignHCenter); verticalLabel2->position->setType(QCPItemPosition::ptAxisRectRatio); verticalLabel2->position->setCoords(0, 0); // 初始位置 verticalLabel2->setText("Time: 0"); verticalLabel2->setFont(QFont(font().family(), 9)); verticalLabel2->setPen(QPen(Qt::green)); verticalLabel2->setBrush(QBrush(Qt::white)); verticalLabel2->setVisible(false); horizontalLabel2 = new QCPItemText(ui->customPlot); horizontalLabel2->setLayer("overlay"); horizontalLabel2->setClipToAxisRect(false); horizontalLabel2->setPositionAlignment(Qt::AlignLeft|Qt::AlignVCenter); horizontalLabel2->position->setType(QCPItemPosition::ptAxisRectRatio); horizontalLabel2->position->setCoords(0, 0); // 初始位置 horizontalLabel2->setText("Voltage: 0"); horizontalLabel2->setFont(QFont(font().family(), 9)); horizontalLabel2->setPen(QPen(Qt::green)); horizontalLabel2->setBrush(QBrush(Qt::white)); horizontalLabel2->setVisible(false); // 创建差值标签 diffLabel = new QCPItemText(ui->customPlot); diffLabel->setLayer("overlay"); diffLabel->setClipToAxisRect(false); diffLabel->setPositionAlignment(Qt::AlignTop|Qt::AlignHCenter); diffLabel->position->setType(QCPItemPosition::ptAxisRectRatio); diffLabel->position->setCoords(0.5, 0.05); // 顶部中间位置 diffLabel->setFont(QFont(font().family(), 10, QFont::Bold)); diffLabel->setPen(QPen(Qt::black)); diffLabel->setBrush(QBrush(Qt::white)); diffLabel->setVisible(false); // 初始化状态 isVerticalDragging1 = false; isHorizontalDragging1 = false; isVerticalDragging2 = false; isHorizontalDragging2 = false; cursorEditMode = false; verticalLine1Created = false; horizontalLine1Created = false; verticalLine2Created = false; horizontalLine2Created = false; // 设置光标模式动作 cursorModeAction = new QAction("光标模式", this); cursorModeAction->setIcon(QIcon(":/images/line_icon/rewind-button.png")); cursorModeAction->setCheckable(true); mainToolBar->addAction(cursorModeAction); connect(cursorModeAction, &QAction::triggered, this, &MainWindow::on_cursorModeAction_triggered); ui->statusbar->showMessage("光标编辑模式已关闭"); // 连接鼠标移动事件到 mouseMoveEvent 槽函数 connect(ui->customPlot, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoveEvent(QMouseEvent*))); connect(ui->customPlot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(mousePressEvent(QMouseEvent*))); connect(ui->customPlot, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleaseEvent(QMouseEvent*))); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_cursorModeAction_triggered() { cursorEditMode = cursorModeAction->isChecked(); if (cursorEditMode) { ui->customPlot->setInteractions(QCP::iSelectPlottables); ui->statusbar->showMessage("光标编辑模式已开启 - 左键添加/移动垂直光标,右键添加/移动水平光标"); } else { ui->customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); ui->statusbar->showMessage("光标编辑模式已关闭"); verticalLine1->setVisible(false); horizontalLine1->setVisible(false); verticalLabel1->setVisible(false); horizontalLabel1->setVisible(false); verticalLine2->setVisible(false); horizontalLine2->setVisible(false); verticalLabel2->setVisible(false); horizontalLabel2->setVisible(false); diffLabel->setVisible(false); verticalLine1Created = false; horizontalLine1Created = false; verticalLine2Created = false; horizontalLine2Created = false; activeVerticalLine = nullptr; activeVerticalLabel = nullptr; activeHorizontalLine = nullptr; activeHorizontalLabel = nullptr; } ui->customPlot->replot(); } // void MainWindow::mousePressEvent(QMouseEvent *event) // { // if (cursorEditMode) { // double x = ui->customPlot->xAxis->pixelToCoord(event->x()); // double y = ui->customPlot->yAxis->pixelToCoord(event->y()); // // 检查点击是否靠近第一组垂直光标 // if (qAbs(x - verticalLine1->point1->coords().x()) < CURSOR_DISTANCE_THRESHOLD) { // isVerticalDragging1 = true; // verticalLine1->setVisible(true); // verticalLabel1->setVisible(true); // verticalLine1Created = true; // } // // 检查点击是否靠近第一组水平光标 // if (qAbs(y - horizontalLine1->point1->coords().y()) < CURSOR_DISTANCE_THRESHOLD) { // isHorizontalDragging1 = true; // horizontalLine1->setVisible(true); // horizontalLabel1->setVisible(true); // horizontalLine1Created = true; // } // // 检查点击是否靠近第二组垂直光标 // if (qAbs(x - verticalLine2->point1->coords().x()) < CURSOR_DISTANCE_THRESHOLD) { // isVerticalDragging2 = true; // verticalLine2->setVisible(true); // verticalLabel2->setVisible(true); // verticalLine2Created = true; // } // // 检查点击是否靠近第二组水平光标 // if (qAbs(y - horizontalLine2->point1->coords().y()) < CURSOR_DISTANCE_THRESHOLD) { // isHorizontalDragging2 = true; // horizontalLine2->setVisible(true); // horizontalLabel2->setVisible(true); // horizontalLine2Created = true; // } // // 如果点击在空白处,根据鼠标位置设置新光标位置 // if (event->button() == Qt::LeftButton) { // if (!verticalLine1Created) { // verticalLine1->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine1->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // verticalLine1->setVisible(true); // verticalLabel1->setVisible(true); // isVerticalDragging1 = true; // verticalLine1Created = true; // } else if (!verticalLine2Created) { // verticalLine2->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine2->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // verticalLine2->setVisible(true); // verticalLabel2->setVisible(true); // isVerticalDragging2 = true; // verticalLine2Created = true; // } // } else if (event->button() == Qt::RightButton) { // if (!horizontalLine1Created) { // horizontalLine1->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine1->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // horizontalLine1->setVisible(true); // horizontalLabel1->setVisible(true); // isHorizontalDragging1 = true; // horizontalLine1Created = true; // } else if (!horizontalLine2Created) { // horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // horizontalLine2->setVisible(true); // horizontalLabel2->setVisible(true); // isHorizontalDragging2 = true; // horizontalLine2Created = true; // } // } // ui->customPlot->replot(); // } else { // QMainWindow::mousePressEvent(event); // } // } // void MainWindow::mousePressEvent(QMouseEvent *event) // { // if (cursorEditMode) { // double x = ui->customPlot->xAxis->pixelToCoord(event->x()); // double y = ui->customPlot->yAxis->pixelToCoord(event->y()); // // 检查点击是否靠近垂直光标 // if (qAbs(x - verticalLine1->point1->coords().x()) < CURSOR_DISTANCE_THRESHOLD) { // activeVerticalLine = verticalLine1; // activeVerticalLabel = verticalLabel1; // isVerticalDragging1 = true; // verticalLine1->setVisible(true); // verticalLabel1->setVisible(true); // verticalLine1Created = true; // } else if (qAbs(x - verticalLine2->point1->coords().x()) < CURSOR_DISTANCE_THRESHOLD) { // activeVerticalLine = verticalLine2; // activeVerticalLabel = verticalLabel2; // isVerticalDragging2 = true; // verticalLine2->setVisible(true); // verticalLabel2->setVisible(true); // verticalLine2Created = true; // } // // 检查点击是否靠近水平光标 // if (qAbs(y - horizontalLine1->point1->coords().y()) < CURSOR_DISTANCE_THRESHOLD) { // activeHorizontalLine = horizontalLine1; // activeHorizontalLabel = horizontalLabel1; // isHorizontalDragging1 = true; // horizontalLine1->setVisible(true); // horizontalLabel1->setVisible(true); // horizontalLine1Created = true; // } else if (qAbs(y - horizontalLine2->point1->coords().y()) < CURSOR_DISTANCE_THRESHOLD) { // activeHorizontalLine = horizontalLine2; // activeHorizontalLabel = horizontalLabel2; // isHorizontalDragging2 = true; // horizontalLine2->setVisible(true); // horizontalLabel2->setVisible(true); // horizontalLine2Created = true; // } // // 如果点击在空白处,根据鼠标位置设置新光标位置 // if (event->button() == Qt::LeftButton) { // if (!verticalLine1Created) { // activeVerticalLine = verticalLine1; // activeVerticalLabel = verticalLabel1; // verticalLine1->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine1->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // verticalLine1->setVisible(true); // verticalLabel1->setVisible(true); // isVerticalDragging1 = true; // verticalLine1Created = true; // } else if (!verticalLine2Created) { // activeVerticalLine = verticalLine2; // activeVerticalLabel = verticalLabel2; // verticalLine2->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine2->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // verticalLine2->setVisible(true); // verticalLabel2->setVisible(true); // isVerticalDragging2 = true; // verticalLine2Created = true; // } // } else if (event->button() == Qt::RightButton) { // if (!horizontalLine1Created) { // activeHorizontalLine = horizontalLine1; // activeHorizontalLabel = horizontalLabel1; // horizontalLine1->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine1->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // horizontalLine1->setVisible(true); // horizontalLabel1->setVisible(true); // isHorizontalDragging1 = true; // horizontalLine1Created = true; // } else if (!horizontalLine2Created) { // activeHorizontalLine = horizontalLine2; // activeHorizontalLabel = horizontalLabel2; // horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // horizontalLine2->setVisible(true); // horizontalLabel2->setVisible(true); // isHorizontalDragging2 = true; // horizontalLine2Created = true; // } // } // ui->customPlot->replot(); // } else { // QMainWindow::mousePressEvent(event); // } // } void MainWindow::mousePressEvent(QMouseEvent *event) { if (cursorEditMode) { double x = ui->customPlot->xAxis->pixelToCoord(event->x()); double y = ui->customPlot->yAxis->pixelToCoord(event->y()); // 重置拖动状态 isVerticalDragging1 = false; isHorizontalDragging1 = false; isVerticalDragging2 = false; isHorizontalDragging2 = false; // 检查垂直光标(仅左键处理) if (event->button() == Qt::LeftButton) { if (verticalLine1Created && qAbs(x - verticalLine1->point1->coords().x()) < CURSOR_DISTANCE_THRESHOLD) { isVerticalDragging1 = true; } else if (verticalLine2Created && qAbs(x - verticalLine2->point1->coords().x()) < CURSOR_DISTANCE_THRESHOLD) { isVerticalDragging2 = true; } } // 检查水平光标(仅右键处理) if (event->button() == Qt::RightButton) { if (horizontalLine1Created && qAbs(y - horizontalLine1->point1->coords().y()) < CURSOR_DISTANCE_THRESHOLD) { isHorizontalDragging1 = true; } else if (horizontalLine2Created && qAbs(y - horizontalLine2->point1->coords().y()) < CURSOR_DISTANCE_THRESHOLD) { isHorizontalDragging2 = true; } } // 如果点击在空白处,根据鼠标位置设置新光标位置 if (event->button() == Qt::LeftButton) { if (!verticalLine1Created) { verticalLine1->point1->setCoords(x, ui->customPlot->yAxis->range().lower); verticalLine1->point2->setCoords(x, ui->customPlot->yAxis->range().upper); verticalLine1->setVisible(true); verticalLabel1->setVisible(true); isVerticalDragging1 = true; verticalLine1Created = true; // 更新标签文本 //verticalLabel1->setText(QString("Time: %1").arg(x, 0, 'f', 2)); updateVerticalCursorLabel(verticalLabel1, x); } else if (!verticalLine2Created) { verticalLine2->point1->setCoords(x, ui->customPlot->yAxis->range().lower); verticalLine2->point2->setCoords(x, ui->customPlot->yAxis->range().upper); verticalLine2->setVisible(true); verticalLabel2->setVisible(true); isVerticalDragging2 = true; verticalLine2Created = true; // 更新标签文本 //verticalLabel1->setText(QString("Time: %1").arg(x, 0, 'f', 2)); updateVerticalCursorLabel(verticalLabel2, x); } } else if (event->button() == Qt::RightButton) { if (!horizontalLine1Created) { horizontalLine1->point1->setCoords(ui->customPlot->xAxis->range().lower, y); horizontalLine1->point2->setCoords(ui->customPlot->xAxis->range().upper, y); horizontalLine1->setVisible(true); horizontalLabel1->setVisible(true); isHorizontalDragging1 = true; horizontalLine1Created = true; // 更新标签文本 //horizontalLabel1->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); updateHorizontalCursorLabel(horizontalLabel1, y); } else if (!horizontalLine2Created) { horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, y); horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, y); horizontalLine2->setVisible(true); horizontalLabel2->setVisible(true); isHorizontalDragging2 = true; horizontalLine2Created = true; // 更新标签文本 //horizontalLabel1->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); updateHorizontalCursorLabel(horizontalLabel2, y); } } ui->customPlot->replot(); } else { QMainWindow::mousePressEvent(event); } } // void MainWindow::mouseMoveEvent(QMouseEvent *event) // { // if (cursorEditMode) { // double x = ui->customPlot->xAxis->pixelToCoord(event->x()); // double y = ui->customPlot->yAxis->pixelToCoord(event->y()); // if (isVerticalDragging1) { // verticalLine1->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine1->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // // 更新垂直标签位置和内容 // verticalLabel1->position->setCoords( // (x - ui->customPlot->xAxis->range().lower) / ui->customPlot->xAxis->range().size(), // 1.0 // ); // verticalLabel1->setText(QString("Time: %1").arg(x, 0, 'f', 2)); // ui->statusbar->showMessage(QString("Time: %1").arg(x, 0, 'f', 2)); // } // if (isHorizontalDragging1) { // horizontalLine1->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine1->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // // 更新水平标签位置和内容 // horizontalLabel1->position->setCoords( // 0.0, // (y - ui->customPlot->yAxis->range().lower) / ui->customPlot->yAxis->range().size() // ); // horizontalLabel1->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); // ui->statusbar->showMessage(QString("Voltage: %1").arg(y, 0, 'f', 2)); // } // if (isVerticalDragging2) { // verticalLine2->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine2->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // // 更新垂直标签位置和内容 // verticalLabel2->position->setCoords( // (x - ui->customPlot->xAxis->range().lower) / ui->customPlot->xAxis->range().size(), // 1.0 // ); // verticalLabel2->setText(QString("Time: %1").arg(x, 0, 'f', 2)); // ui->statusbar->showMessage(QString("Time: %1").arg(x, 0, 'f', 2)); // } // if (isHorizontalDragging2) { // horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // // 更新水平标签位置和内容 // horizontalLabel2->position->setCoords( // 0.0, // (y - ui->customPlot->yAxis->range().lower) / ui->customPlot->yAxis->range().size() // ); // horizontalLabel2->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); // ui->statusbar->showMessage(QString("Voltage: %1").arg(y, 0, 'f', 2)); // } // ui->customPlot->replot(); // calculateAndDisplayDifferences(); // } else { // QMainWindow::mouseMoveEvent(event); // } // } // void MainWindow::mouseMoveEvent(QMouseEvent *event) // { // if (cursorEditMode) { // double x = ui->customPlot->xAxis->pixelToCoord(event->x()); // double y = ui->customPlot->yAxis->pixelToCoord(event->y()); // if (isVerticalDragging1 || isVerticalDragging2) { // activeVerticalLine->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // activeVerticalLine->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // // 更新垂直标签位置和内容 // activeVerticalLabel->position->setCoords( // (x - ui->customPlot->xAxis->range().lower) / ui->customPlot->xAxis->range().size(), // 1.0 // ); // activeVerticalLabel->setText(QString("Time: %1").arg(x, 0, 'f', 2)); // ui->statusbar->showMessage(QString("Time: %1").arg(x, 0, 'f', 2)); // } // if (isHorizontalDragging1 || isHorizontalDragging2) { // activeHorizontalLine->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // activeHorizontalLine->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // // 更新水平标签位置和内容 // activeHorizontalLabel->position->setCoords( // 0.0, // (y - ui->customPlot->yAxis->range().lower) / ui->customPlot->yAxis->range().size() // ); // activeHorizontalLabel->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); // ui->statusbar->showMessage(QString("Voltage: %1").arg(y, 0, 'f', 2)); // } // ui->customPlot->replot(); // calculateAndDisplayDifferences(); // } else { // QMainWindow::mouseMoveEvent(event); // } // } void MainWindow::mouseMoveEvent(QMouseEvent *event) { if (cursorEditMode) { double x = ui->customPlot->xAxis->pixelToCoord(event->x()); double y = ui->customPlot->yAxis->pixelToCoord(event->y()); // 独立处理垂直光标拖动 if (isVerticalDragging1) { // verticalLine1->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine1->point2->setCoords(x, ui->customPlot->yAxis->range().upper); verticalLine1->point1->setCoords(x, verticalLine1->point1->coords().y()); verticalLine1->point2->setCoords(x, verticalLine1->point2->coords().y()); verticalLabel1->position->setCoords( (x - ui->customPlot->xAxis->range().lower) / ui->customPlot->xAxis->range().size(), 1.0); verticalLabel1->setText(QString("Time: %1").arg(x, 0, 'f', 2)); } else if (isVerticalDragging2) { // verticalLine2->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine2->point2->setCoords(x, ui->customPlot->yAxis->range().upper); verticalLine2->point1->setCoords(x, verticalLine2->point1->coords().y()); verticalLine2->point2->setCoords(x, verticalLine2->point2->coords().y()); verticalLabel2->position->setCoords( (x - ui->customPlot->xAxis->range().lower) / ui->customPlot->xAxis->range().size(), 1.0); verticalLabel2->setText(QString("Time: %1").arg(x, 0, 'f', 2)); } // 独立处理水平光标拖动 if (isHorizontalDragging1) { // horizontalLine1->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine1->point2->setCoords(ui->customPlot->xAxis->range().upper, y); horizontalLine1->point1->setCoords(horizontalLine1->point1->coords().x(), y); horizontalLine1->point2->setCoords(horizontalLine2->point2->coords().x(), y); horizontalLabel1->position->setCoords( 0.0, (y - ui->customPlot->yAxis->range().lower) / ui->customPlot->yAxis->range().size()); horizontalLabel1->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); } else if (isHorizontalDragging2) { // horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, y); horizontalLine2->point1->setCoords(horizontalLine2->point1->coords().x(), y); horizontalLine2->point2->setCoords(horizontalLine2->point2->coords().x(), y); horizontalLabel2->position->setCoords( 0.0, (y - ui->customPlot->yAxis->range().lower) / ui->customPlot->yAxis->range().size()); horizontalLabel2->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); } ui->customPlot->replot(); calculateAndDisplayDifferences(); qDebug() << "Creating horizontal line 2 at y:" << y; } else { QMainWindow::mouseMoveEvent(event); } } void MainWindow::mouseReleaseEvent(QMouseEvent *event) { isVerticalDragging1 = false; isHorizontalDragging1 = false; isVerticalDragging2 = false; isHorizontalDragging2 = false; if (!cursorEditMode) { QMainWindow::mouseReleaseEvent(event); } } void MainWindow::calculateAndDisplayDifferences() { static double lastTimeDiff = 0; static double lastVoltageDiff = 0; QString statusMessage; bool needsUpdate = false; // 计算并显示时间差值 if (verticalLine1->visible() && verticalLine2->visible()) { double timeDiff = qAbs(verticalLine1->point1->coords().x() - verticalLine2->point1->coords().x()); if (!qFuzzyCompare(timeDiff, lastTimeDiff)) { statusMessage += QString("ΔT: %1 ").arg(timeDiff, 0, 'f', 2); lastTimeDiff = timeDiff; needsUpdate = true; } } // 计算并显示电压差值 if (horizontalLine1->visible() && horizontalLine2->visible()) { double voltageDiff = qAbs(horizontalLine1->point1->coords().y() - horizontalLine2->point1->coords().y()); if (!qFuzzyCompare(voltageDiff, lastVoltageDiff)) { statusMessage += QString("ΔV: %1").arg(voltageDiff, 0, 'f', 2); lastVoltageDiff = voltageDiff; needsUpdate = true; } } // 仅在需要时更新UI if (needsUpdate) { ui->statusbar->showMessage(statusMessage); if (diffLabel) { diffLabel->setText(statusMessage); diffLabel->setVisible(!statusMessage.isEmpty()); } ui->customPlot->replot(QCustomPlot::rpQueuedReplot); } } void MainWindow::updateVerticalCursorLabel(QCPItemText *label, double x) { label->position->setCoords( (x - ui->customPlot->xAxis->range().lower) / ui->customPlot->xAxis->range().size(), 1.0); label->setText(QString("Time: %1").arg(x, 0, 'f', 2)); } void MainWindow::updateHorizontalCursorLabel(QCPItemText *label, double y) { label->position->setCoords( 0.0, (y - ui->customPlot->yAxis->range().lower) / ui->customPlot->yAxis->range().size()); label->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); qDebug() << "Setting horizontal label 2 text to voltage:" << y; } #include "mainwindow.h" #include "ui_Mainwindow.h" #include "qcustomplot.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); // 创建主工具栏 mainToolBar = new QToolBar("主工具栏", this); mainToolBar->setMovable(false); // 禁止工具栏移动 addToolBar(Qt::TopToolBarArea, mainToolBar); ui->customPlot->addGraph(); ui->customPlot->graph(0)->setPen(QPen(Qt::blue)); // 线条颜色为蓝色 // 生成一些数据点 QVector<double> x(251), y0(251); for (int i=0; i<251; ++i) { x[i] = i; y0[i] = qExp(-i/150.0)*qCos(i/10.0); // 指数衰减的余弦函数 } // 配置右轴和上轴显示刻度但不显示标签 ui->customPlot->xAxis2->setVisible(true); ui->customPlot->xAxis2->setTickLabels(false); ui->customPlot->yAxis2->setVisible(true); ui->customPlot->yAxis2->setTickLabels(false); // 使左轴和下轴的范围变化同步到右轴和上轴 connect(ui->customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), ui->customPlot->xAxis2, SLOT(setRange(QCPRange))); connect(ui->customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), ui->customPlot->yAxis2, SLOT(setRange(QCPRange))); // 将数据点传递给形 ui->customPlot->graph(0)->setData(x, y0); // 自动调整坐标轴范围以适应形 ui->customPlot->graph(0)->rescaleAxes(); // 允许用户通过鼠标拖动坐标轴范围、使用鼠标滚轮缩放以及通过点击选择形 ui->customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); // 计算表中心位置 double xCenter = (ui->customPlot->xAxis->range().lower + ui->customPlot->xAxis->range().upper) / 2; double yCenter = (ui->customPlot->yAxis->range().lower + ui->customPlot->yAxis->range().upper) / 2; // 初始化第一组纵向和横向光标线 verticalLine1 = new QCPItemStraightLine(ui->customPlot); verticalLine1->setPen(QPen(Qt::red)); verticalLine1->point1->setCoords(0, ui->customPlot->yAxis->range().lower); verticalLine1->point2->setCoords(0, ui->customPlot->yAxis->range().upper); verticalLine1->setVisible(false); horizontalLine1 = new QCPItemStraightLine(ui->customPlot); horizontalLine1->setPen(QPen(Qt::red)); horizontalLine1->point1->setCoords(ui->customPlot->xAxis->range().lower, 0); horizontalLine1->point2->setCoords(ui->customPlot->xAxis->range().upper, 0); horizontalLine1->setVisible(false); // 初始化第一组标签 verticalLabel1 = new QCPItemText(ui->customPlot); verticalLabel1->setLayer("overlay"); // 确保标签显示在最上层 verticalLabel1->setClipToAxisRect(false); verticalLabel1->setPositionAlignment(Qt::AlignBottom|Qt::AlignHCenter); verticalLabel1->position->setType(QCPItemPosition::ptAxisRectRatio); verticalLabel1->position->setCoords(0, 0); // 初始位置 verticalLabel1->setText("Time: 0"); verticalLabel1->setFont(QFont(font().family(), 9)); verticalLabel1->setPen(QPen(Qt::red)); verticalLabel1->setBrush(QBrush(Qt::white)); verticalLabel1->setVisible(false); horizontalLabel1 = new QCPItemText(ui->customPlot); horizontalLabel1->setLayer("overlay"); horizontalLabel1->setClipToAxisRect(false); horizontalLabel1->setPositionAlignment(Qt::AlignLeft|Qt::AlignVCenter); horizontalLabel1->position->setType(QCPItemPosition::ptAxisRectRatio); horizontalLabel1->position->setCoords(0, 0); // 初始位置 horizontalLabel1->setText("Voltage: 0"); horizontalLabel1->setFont(QFont(font().family(), 9)); horizontalLabel1->setPen(QPen(Qt::red)); horizontalLabel1->setBrush(QBrush(Qt::white)); horizontalLabel1->setVisible(false); // 初始化第二组纵向和横向光标线 verticalLine2 = new QCPItemStraightLine(ui->customPlot); verticalLine2->setPen(QPen(Qt::green)); // verticalLine2->point1->setCoords(0, ui->customPlot->yAxis->range().lower); // verticalLine2->point2->setCoords(0, ui->customPlot->yAxis->range().upper); verticalLine2->point1->setCoords(xCenter + 10, ui->customPlot->yAxis->range().lower); verticalLine2->point2->setCoords(xCenter + 10, ui->customPlot->yAxis->range().upper); verticalLine2->setVisible(false); horizontalLine2 = new QCPItemStraightLine(ui->customPlot); horizontalLine2->setPen(QPen(Qt::green)); // horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, 0); // horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, 0); horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, yCenter + 0.2); horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, yCenter + 0.2); horizontalLine2->setVisible(false); // 初始化第二组标签 verticalLabel2 = new QCPItemText(ui->customPlot); verticalLabel2->setLayer("overlay"); // 确保标签显示在最上层 verticalLabel2->setClipToAxisRect(false); verticalLabel2->setPositionAlignment(Qt::AlignBottom|Qt::AlignHCenter); verticalLabel2->position->setType(QCPItemPosition::ptAxisRectRatio); verticalLabel2->position->setCoords(0, 0); // 初始位置 verticalLabel2->setText("Time: 0"); verticalLabel2->setFont(QFont(font().family(), 9)); verticalLabel2->setPen(QPen(Qt::green)); verticalLabel2->setBrush(QBrush(Qt::white)); verticalLabel2->setVisible(false); horizontalLabel2 = new QCPItemText(ui->customPlot); horizontalLabel2->setLayer("overlay"); horizontalLabel2->setClipToAxisRect(false); horizontalLabel2->setPositionAlignment(Qt::AlignLeft|Qt::AlignVCenter); horizontalLabel2->position->setType(QCPItemPosition::ptAxisRectRatio); horizontalLabel2->position->setCoords(0, 0); // 初始位置 horizontalLabel2->setText("Voltage: 0"); horizontalLabel2->setFont(QFont(font().family(), 9)); horizontalLabel2->setPen(QPen(Qt::green)); horizontalLabel2->setBrush(QBrush(Qt::white)); horizontalLabel2->setVisible(false); // 创建差值标签 diffLabel = new QCPItemText(ui->customPlot); diffLabel->setLayer("overlay"); diffLabel->setClipToAxisRect(false); diffLabel->setPositionAlignment(Qt::AlignTop|Qt::AlignHCenter); diffLabel->position->setType(QCPItemPosition::ptAxisRectRatio); diffLabel->position->setCoords(0.5, 0.05); // 顶部中间位置 diffLabel->setFont(QFont(font().family(), 10, QFont::Bold)); diffLabel->setPen(QPen(Qt::black)); diffLabel->setBrush(QBrush(Qt::white)); diffLabel->setVisible(false); // 初始化状态 isVerticalDragging1 = false; isHorizontalDragging1 = false; isVerticalDragging2 = false; isHorizontalDragging2 = false; cursorEditMode = false; verticalLine1Created = false; horizontalLine1Created = false; verticalLine2Created = false; horizontalLine2Created = false; // 设置光标模式动作 cursorModeAction = new QAction("光标模式", this); cursorModeAction->setIcon(QIcon(":/images/line_icon/rewind-button.png")); cursorModeAction->setCheckable(true); mainToolBar->addAction(cursorModeAction); connect(cursorModeAction, &QAction::triggered, this, &MainWindow::on_cursorModeAction_triggered); ui->statusbar->showMessage("光标编辑模式已关闭"); // 连接鼠标移动事件到 mouseMoveEvent 槽函数 connect(ui->customPlot, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoveEvent(QMouseEvent*))); connect(ui->customPlot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(mousePressEvent(QMouseEvent*))); connect(ui->customPlot, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleaseEvent(QMouseEvent*))); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_cursorModeAction_triggered() { cursorEditMode = cursorModeAction->isChecked(); if (cursorEditMode) { ui->customPlot->setInteractions(QCP::iSelectPlottables); ui->statusbar->showMessage("光标编辑模式已开启 - 左键添加/移动垂直光标,右键添加/移动水平光标"); } else { ui->customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); ui->statusbar->showMessage("光标编辑模式已关闭"); verticalLine1->setVisible(false); horizontalLine1->setVisible(false); verticalLabel1->setVisible(false); horizontalLabel1->setVisible(false); verticalLine2->setVisible(false); horizontalLine2->setVisible(false); verticalLabel2->setVisible(false); horizontalLabel2->setVisible(false); diffLabel->setVisible(false); verticalLine1Created = false; horizontalLine1Created = false; verticalLine2Created = false; horizontalLine2Created = false; activeVerticalLine = nullptr; activeVerticalLabel = nullptr; activeHorizontalLine = nullptr; activeHorizontalLabel = nullptr; } ui->customPlot->replot(); } // void MainWindow::mousePressEvent(QMouseEvent *event) // { // if (cursorEditMode) { // double x = ui->customPlot->xAxis->pixelToCoord(event->x()); // double y = ui->customPlot->yAxis->pixelToCoord(event->y()); // // 检查点击是否靠近第一组垂直光标 // if (qAbs(x - verticalLine1->point1->coords().x()) < CURSOR_DISTANCE_THRESHOLD) { // isVerticalDragging1 = true; // verticalLine1->setVisible(true); // verticalLabel1->setVisible(true); // verticalLine1Created = true; // } // // 检查点击是否靠近第一组水平光标 // if (qAbs(y - horizontalLine1->point1->coords().y()) < CURSOR_DISTANCE_THRESHOLD) { // isHorizontalDragging1 = true; // horizontalLine1->setVisible(true); // horizontalLabel1->setVisible(true); // horizontalLine1Created = true; // } // // 检查点击是否靠近第二组垂直光标 // if (qAbs(x - verticalLine2->point1->coords().x()) < CURSOR_DISTANCE_THRESHOLD) { // isVerticalDragging2 = true; // verticalLine2->setVisible(true); // verticalLabel2->setVisible(true); // verticalLine2Created = true; // } // // 检查点击是否靠近第二组水平光标 // if (qAbs(y - horizontalLine2->point1->coords().y()) < CURSOR_DISTANCE_THRESHOLD) { // isHorizontalDragging2 = true; // horizontalLine2->setVisible(true); // horizontalLabel2->setVisible(true); // horizontalLine2Created = true; // } // // 如果点击在空白处,根据鼠标位置设置新光标位置 // if (event->button() == Qt::LeftButton) { // if (!verticalLine1Created) { // verticalLine1->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine1->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // verticalLine1->setVisible(true); // verticalLabel1->setVisible(true); // isVerticalDragging1 = true; // verticalLine1Created = true; // } else if (!verticalLine2Created) { // verticalLine2->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine2->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // verticalLine2->setVisible(true); // verticalLabel2->setVisible(true); // isVerticalDragging2 = true; // verticalLine2Created = true; // } // } else if (event->button() == Qt::RightButton) { // if (!horizontalLine1Created) { // horizontalLine1->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine1->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // horizontalLine1->setVisible(true); // horizontalLabel1->setVisible(true); // isHorizontalDragging1 = true; // horizontalLine1Created = true; // } else if (!horizontalLine2Created) { // horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // horizontalLine2->setVisible(true); // horizontalLabel2->setVisible(true); // isHorizontalDragging2 = true; // horizontalLine2Created = true; // } // } // ui->customPlot->replot(); // } else { // QMainWindow::mousePressEvent(event); // } // } // void MainWindow::mousePressEvent(QMouseEvent *event) // { // if (cursorEditMode) { // double x = ui->customPlot->xAxis->pixelToCoord(event->x()); // double y = ui->customPlot->yAxis->pixelToCoord(event->y()); // // 检查点击是否靠近垂直光标 // if (qAbs(x - verticalLine1->point1->coords().x()) < CURSOR_DISTANCE_THRESHOLD) { // activeVerticalLine = verticalLine1; // activeVerticalLabel = verticalLabel1; // isVerticalDragging1 = true; // verticalLine1->setVisible(true); // verticalLabel1->setVisible(true); // verticalLine1Created = true; // } else if (qAbs(x - verticalLine2->point1->coords().x()) < CURSOR_DISTANCE_THRESHOLD) { // activeVerticalLine = verticalLine2; // activeVerticalLabel = verticalLabel2; // isVerticalDragging2 = true; // verticalLine2->setVisible(true); // verticalLabel2->setVisible(true); // verticalLine2Created = true; // } // // 检查点击是否靠近水平光标 // if (qAbs(y - horizontalLine1->point1->coords().y()) < CURSOR_DISTANCE_THRESHOLD) { // activeHorizontalLine = horizontalLine1; // activeHorizontalLabel = horizontalLabel1; // isHorizontalDragging1 = true; // horizontalLine1->setVisible(true); // horizontalLabel1->setVisible(true); // horizontalLine1Created = true; // } else if (qAbs(y - horizontalLine2->point1->coords().y()) < CURSOR_DISTANCE_THRESHOLD) { // activeHorizontalLine = horizontalLine2; // activeHorizontalLabel = horizontalLabel2; // isHorizontalDragging2 = true; // horizontalLine2->setVisible(true); // horizontalLabel2->setVisible(true); // horizontalLine2Created = true; // } // // 如果点击在空白处,根据鼠标位置设置新光标位置 // if (event->button() == Qt::LeftButton) { // if (!verticalLine1Created) { // activeVerticalLine = verticalLine1; // activeVerticalLabel = verticalLabel1; // verticalLine1->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine1->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // verticalLine1->setVisible(true); // verticalLabel1->setVisible(true); // isVerticalDragging1 = true; // verticalLine1Created = true; // } else if (!verticalLine2Created) { // activeVerticalLine = verticalLine2; // activeVerticalLabel = verticalLabel2; // verticalLine2->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine2->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // verticalLine2->setVisible(true); // verticalLabel2->setVisible(true); // isVerticalDragging2 = true; // verticalLine2Created = true; // } // } else if (event->button() == Qt::RightButton) { // if (!horizontalLine1Created) { // activeHorizontalLine = horizontalLine1; // activeHorizontalLabel = horizontalLabel1; // horizontalLine1->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine1->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // horizontalLine1->setVisible(true); // horizontalLabel1->setVisible(true); // isHorizontalDragging1 = true; // horizontalLine1Created = true; // } else if (!horizontalLine2Created) { // activeHorizontalLine = horizontalLine2; // activeHorizontalLabel = horizontalLabel2; // horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // horizontalLine2->setVisible(true); // horizontalLabel2->setVisible(true); // isHorizontalDragging2 = true; // horizontalLine2Created = true; // } // } // ui->customPlot->replot(); // } else { // QMainWindow::mousePressEvent(event); // } // } void MainWindow::mousePressEvent(QMouseEvent *event) { if (cursorEditMode) { double x = ui->customPlot->xAxis->pixelToCoord(event->x()); double y = ui->customPlot->yAxis->pixelToCoord(event->y()); // 重置拖动状态 isVerticalDragging1 = false; isHorizontalDragging1 = false; isVerticalDragging2 = false; isHorizontalDragging2 = false; // 检查垂直光标(仅左键处理) if (event->button() == Qt::LeftButton) { if (verticalLine1Created && qAbs(x - verticalLine1->point1->coords().x()) < CURSOR_DISTANCE_THRESHOLD) { isVerticalDragging1 = true; } else if (verticalLine2Created && qAbs(x - verticalLine2->point1->coords().x()) < CURSOR_DISTANCE_THRESHOLD) { isVerticalDragging2 = true; } } // 检查水平光标(仅右键处理) if (event->button() == Qt::RightButton) { if (horizontalLine1Created && qAbs(y - horizontalLine1->point1->coords().y()) < CURSOR_DISTANCE_THRESHOLD) { isHorizontalDragging1 = true; } else if (horizontalLine2Created && qAbs(y - horizontalLine2->point1->coords().y()) < CURSOR_DISTANCE_THRESHOLD) { isHorizontalDragging2 = true; } } // 如果点击在空白处,根据鼠标位置设置新光标位置 if (event->button() == Qt::LeftButton) { if (!verticalLine1Created) { verticalLine1->point1->setCoords(x, ui->customPlot->yAxis->range().lower); verticalLine1->point2->setCoords(x, ui->customPlot->yAxis->range().upper); verticalLine1->setVisible(true); verticalLabel1->setVisible(true); isVerticalDragging1 = true; verticalLine1Created = true; // 更新标签文本 //verticalLabel1->setText(QString("Time: %1").arg(x, 0, 'f', 2)); updateVerticalCursorLabel(verticalLabel1, x); } else if (!verticalLine2Created) { verticalLine2->point1->setCoords(x, ui->customPlot->yAxis->range().lower); verticalLine2->point2->setCoords(x, ui->customPlot->yAxis->range().upper); verticalLine2->setVisible(true); verticalLabel2->setVisible(true); isVerticalDragging2 = true; verticalLine2Created = true; // 更新标签文本 //verticalLabel1->setText(QString("Time: %1").arg(x, 0, 'f', 2)); updateVerticalCursorLabel(verticalLabel2, x); } } else if (event->button() == Qt::RightButton) { if (!horizontalLine1Created) { horizontalLine1->point1->setCoords(ui->customPlot->xAxis->range().lower, y); horizontalLine1->point2->setCoords(ui->customPlot->xAxis->range().upper, y); horizontalLine1->setVisible(true); horizontalLabel1->setVisible(true); isHorizontalDragging1 = true; horizontalLine1Created = true; // 更新标签文本 //horizontalLabel1->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); updateHorizontalCursorLabel(horizontalLabel1, y); } else if (!horizontalLine2Created) { horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, y); horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, y); horizontalLine2->setVisible(true); horizontalLabel2->setVisible(true); isHorizontalDragging2 = true; horizontalLine2Created = true; // 更新标签文本 //horizontalLabel1->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); updateHorizontalCursorLabel(horizontalLabel2, y); } } ui->customPlot->replot(); } else { QMainWindow::mousePressEvent(event); } } // void MainWindow::mouseMoveEvent(QMouseEvent *event) // { // if (cursorEditMode) { // double x = ui->customPlot->xAxis->pixelToCoord(event->x()); // double y = ui->customPlot->yAxis->pixelToCoord(event->y()); // if (isVerticalDragging1) { // verticalLine1->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine1->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // // 更新垂直标签位置和内容 // verticalLabel1->position->setCoords( // (x - ui->customPlot->xAxis->range().lower) / ui->customPlot->xAxis->range().size(), // 1.0 // ); // verticalLabel1->setText(QString("Time: %1").arg(x, 0, 'f', 2)); // ui->statusbar->showMessage(QString("Time: %1").arg(x, 0, 'f', 2)); // } // if (isHorizontalDragging1) { // horizontalLine1->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine1->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // // 更新水平标签位置和内容 // horizontalLabel1->position->setCoords( // 0.0, // (y - ui->customPlot->yAxis->range().lower) / ui->customPlot->yAxis->range().size() // ); // horizontalLabel1->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); // ui->statusbar->showMessage(QString("Voltage: %1").arg(y, 0, 'f', 2)); // } // if (isVerticalDragging2) { // verticalLine2->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine2->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // // 更新垂直标签位置和内容 // verticalLabel2->position->setCoords( // (x - ui->customPlot->xAxis->range().lower) / ui->customPlot->xAxis->range().size(), // 1.0 // ); // verticalLabel2->setText(QString("Time: %1").arg(x, 0, 'f', 2)); // ui->statusbar->showMessage(QString("Time: %1").arg(x, 0, 'f', 2)); // } // if (isHorizontalDragging2) { // horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // // 更新水平标签位置和内容 // horizontalLabel2->position->setCoords( // 0.0, // (y - ui->customPlot->yAxis->range().lower) / ui->customPlot->yAxis->range().size() // ); // horizontalLabel2->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); // ui->statusbar->showMessage(QString("Voltage: %1").arg(y, 0, 'f', 2)); // } // ui->customPlot->replot(); // calculateAndDisplayDifferences(); // } else { // QMainWindow::mouseMoveEvent(event); // } // } // void MainWindow::mouseMoveEvent(QMouseEvent *event) // { // if (cursorEditMode) { // double x = ui->customPlot->xAxis->pixelToCoord(event->x()); // double y = ui->customPlot->yAxis->pixelToCoord(event->y()); // if (isVerticalDragging1 || isVerticalDragging2) { // activeVerticalLine->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // activeVerticalLine->point2->setCoords(x, ui->customPlot->yAxis->range().upper); // // 更新垂直标签位置和内容 // activeVerticalLabel->position->setCoords( // (x - ui->customPlot->xAxis->range().lower) / ui->customPlot->xAxis->range().size(), // 1.0 // ); // activeVerticalLabel->setText(QString("Time: %1").arg(x, 0, 'f', 2)); // ui->statusbar->showMessage(QString("Time: %1").arg(x, 0, 'f', 2)); // } // if (isHorizontalDragging1 || isHorizontalDragging2) { // activeHorizontalLine->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // activeHorizontalLine->point2->setCoords(ui->customPlot->xAxis->range().upper, y); // // 更新水平标签位置和内容 // activeHorizontalLabel->position->setCoords( // 0.0, // (y - ui->customPlot->yAxis->range().lower) / ui->customPlot->yAxis->range().size() // ); // activeHorizontalLabel->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); // ui->statusbar->showMessage(QString("Voltage: %1").arg(y, 0, 'f', 2)); // } // ui->customPlot->replot(); // calculateAndDisplayDifferences(); // } else { // QMainWindow::mouseMoveEvent(event); // } // } void MainWindow::mouseMoveEvent(QMouseEvent *event) { if (cursorEditMode) { double x = ui->customPlot->xAxis->pixelToCoord(event->x()); double y = ui->customPlot->yAxis->pixelToCoord(event->y()); // 独立处理垂直光标拖动 if (isVerticalDragging1) { // verticalLine1->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine1->point2->setCoords(x, ui->customPlot->yAxis->range().upper); verticalLine1->point1->setCoords(x, verticalLine1->point1->coords().y()); verticalLine1->point2->setCoords(x, verticalLine1->point2->coords().y()); verticalLabel1->position->setCoords( (x - ui->customPlot->xAxis->range().lower) / ui->customPlot->xAxis->range().size(), 1.0); verticalLabel1->setText(QString("Time: %1").arg(x, 0, 'f', 2)); } else if (isVerticalDragging2) { // verticalLine2->point1->setCoords(x, ui->customPlot->yAxis->range().lower); // verticalLine2->point2->setCoords(x, ui->customPlot->yAxis->range().upper); verticalLine2->point1->setCoords(x, verticalLine2->point1->coords().y()); verticalLine2->point2->setCoords(x, verticalLine2->point2->coords().y()); verticalLabel2->position->setCoords( (x - ui->customPlot->xAxis->range().lower) / ui->customPlot->xAxis->range().size(), 1.0); verticalLabel2->setText(QString("Time: %1").arg(x, 0, 'f', 2)); } // 独立处理水平光标拖动 if (isHorizontalDragging1) { // horizontalLine1->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine1->point2->setCoords(ui->customPlot->xAxis->range().upper, y); horizontalLine1->point1->setCoords(horizontalLine1->point1->coords().x(), y); horizontalLine1->point2->setCoords(horizontalLine2->point2->coords().x(), y); horizontalLabel1->position->setCoords( 0.0, (y - ui->customPlot->yAxis->range().lower) / ui->customPlot->yAxis->range().size()); horizontalLabel1->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); } else if (isHorizontalDragging2) { // horizontalLine2->point1->setCoords(ui->customPlot->xAxis->range().lower, y); // horizontalLine2->point2->setCoords(ui->customPlot->xAxis->range().upper, y); horizontalLine2->point1->setCoords(horizontalLine2->point1->coords().x(), y); horizontalLine2->point2->setCoords(horizontalLine2->point2->coords().x(), y); horizontalLabel2->position->setCoords( 0.0, (y - ui->customPlot->yAxis->range().lower) / ui->customPlot->yAxis->range().size()); horizontalLabel2->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); } ui->customPlot->replot(); calculateAndDisplayDifferences(); qDebug() << "Creating horizontal line 2 at y:" << y; } else { QMainWindow::mouseMoveEvent(event); } } void MainWindow::mouseReleaseEvent(QMouseEvent *event) { isVerticalDragging1 = false; isHorizontalDragging1 = false; isVerticalDragging2 = false; isHorizontalDragging2 = false; if (!cursorEditMode) { QMainWindow::mouseReleaseEvent(event); } } void MainWindow::calculateAndDisplayDifferences() { static double lastTimeDiff = 0; static double lastVoltageDiff = 0; QString statusMessage; bool needsUpdate = false; // 计算并显示时间差值 if (verticalLine1->visible() && verticalLine2->visible()) { double timeDiff = qAbs(verticalLine1->point1->coords().x() - verticalLine2->point1->coords().x()); if (!qFuzzyCompare(timeDiff, lastTimeDiff)) { statusMessage += QString("ΔT: %1 ").arg(timeDiff, 0, 'f', 2); lastTimeDiff = timeDiff; needsUpdate = true; } } // 计算并显示电压差值 if (horizontalLine1->visible() && horizontalLine2->visible()) { double voltageDiff = qAbs(horizontalLine1->point1->coords().y() - horizontalLine2->point1->coords().y()); if (!qFuzzyCompare(voltageDiff, lastVoltageDiff)) { statusMessage += QString("ΔV: %1").arg(voltageDiff, 0, 'f', 2); lastVoltageDiff = voltageDiff; needsUpdate = true; } } // 仅在需要时更新UI if (needsUpdate) { ui->statusbar->showMessage(statusMessage); if (diffLabel) { diffLabel->setText(statusMessage); diffLabel->setVisible(!statusMessage.isEmpty()); } ui->customPlot->replot(QCustomPlot::rpQueuedReplot); } } void MainWindow::updateVerticalCursorLabel(QCPItemText *label, double x) { label->position->setCoords( (x - ui->customPlot->xAxis->range().lower) / ui->customPlot->xAxis->range().size(), 1.0); label->setText(QString("Time: %1").arg(x, 0, 'f', 2)); } void MainWindow::updateHorizontalCursorLabel(QCPItemText *label, double y) { label->position->setCoords( 0.0, (y - ui->customPlot->yAxis->range().lower) / ui->customPlot->yAxis->range().size()); label->setText(QString("Voltage: %1").arg(y, 0, 'f', 2)); qDebug() << "Setting horizontal label 2 text to voltage:" << y; } 在QCustomPlot中实现双光标测量两点之间的差值
最新发布
07-10
<think>我们将在QCustomPlot中实现双光标测量功能,允许用户通过两次点击(或按键)确定两个光标位置,然后计算并显示两点之间的时间差(X轴差值)和电压差(Y轴差值)。以下是实现步骤: ### 1. 定义光标类 首先,我们可以定义一个光标类,用于管理每个光标的属性和方法。每个光标包含一条垂直线(用于标记X位置)和可选的文本标签(显示坐标和测量值)。 ```cpp class Cursor { public: Cursor(QCustomPlot *plot, QCPAxis *keyAxis, QCPAxis *valueAxis); void setPosition(double x); double position() const { return m_x; } void setVisible(bool visible); void updateLabel(); // 其他方法... private: QCustomPlot *m_plot; QCPItemStraightLine *m_line; // 垂直线 QCPItemText *m_label; // 文本标签 double m_x; }; ``` ### 2. 双光标管理 在自定义的QCustomPlot子类中,管理两个光标(Cursor1和Cursor2)以及一个用于显示差值的文本项。 ```cpp class MyCustomPlot : public QCustomPlot { Q_OBJECT public: MyCustomPlot(QWidget *parent = nullptr); // ... 其他成员函数 protected: void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; private: Cursor *m_cursor1; Cursor *m_cursor2; QCPItemText *m_deltaLabel; // 显示差值 int m_currentCursorIndex; // 当前操作的光标索引(0表示光标1,1表示光标2) void updateDeltaLabel(); // 更新差值显示 }; ``` ### 3. 鼠标事件处理 - **鼠标移动事件**:移动当前选中的光标(通过`m_currentCursorIndex`指定),并更新光标位置和差值显示。 - **鼠标点击事件**:切换当前操作的光标(点击一次切换一个光标),并将光标移动到当前鼠标位置。 ```cpp void MyCustomPlot::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { // 切换当前操作的光标 m_currentCursorIndex = (m_currentCursorIndex + 1) % 2; double x = xAxis->pixelToCoord(event->pos().x()); // 设置当前光标位置 if (m_currentCursorIndex == 0) { m_cursor1->setPosition(x); } else { m_cursor2->setPosition(x); } updateDeltaLabel(); // 更新差值 replot(); } QCustomPlot::mousePressEvent(event); // 确保其他事件(如缩放)正常 } void MyCustomPlot::mouseMoveEvent(QMouseEvent *event) { double x = xAxis->pixelToCoord(event->pos().x()); // 移动当前选中的光标 if (m_currentCursorIndex == 0) { m_cursor1->setPosition(x); } else { m_cursor2->setPosition(x); } updateDeltaLabel(); replot(); QCustomPlot::mouseMoveEvent(event); } ``` ### 4. 差值计算显示 计算两个光标之间的X轴差值(时间差)和Y轴差值(电压差)。注意,这里Y轴差值需要根据实际数据计算(因为光标位置在X轴,而Y值需要从数据中获取)。 ```cpp void MyCustomPlot::updateDeltaLabel() { double x1 = m_cursor1->position(); double x2 = m_cursor2->position(); double deltaX = qAbs(x2 - x1); // 获取y值:假设我们只有一个形,且数据已排序 QCPGraph *graph = this->graph(0); double y1 = graph->data()->at(x1)->value; double y2 = graph->data()->at(x2)->value; double deltaY = qAbs(y2 - y1); // 更新差值标签 m_deltaLabel->setText(QString("Δt=%1, ΔV=%2").arg(deltaX).arg(deltaY)); // 设置标签位置(例如,放在两个光标中间) double midX = (x1 + x2) / 2; double midY = (y1 + y2) / 2; m_deltaLabel->position->setCoords(midX, midY); } ``` ### 5. 初始化光标设置 在构造函数中初始化两个光标和差值标签,并设置初始状态。 ```cpp MyCustomPlot::MyCustomPlot(QWidget *parent) : QCustomPlot(parent), m_currentCursorIndex(0) { // 创建两个光标 m_cursor1 = new Cursor(this, xAxis, yAxis); m_cursor2 = new Cursor(this, xAxis, yAxis); // 设置光标颜色等属性 // 创建差值标签 m_deltaLabel = new QCPItemText(this); m_deltaLabel->setVisible(false); // 其他初始化... } ``` ### 6. 光标类实现 下面是Cursor类的构造函数和设置位置的方法: ```cpp Cursor::Cursor(QCustomPlot *plot, QCPAxis *keyAxis, QCPAxis *valueAxis) : m_plot(plot), m_x(0) { m_line = new QCPItemStraightLine(plot); m_line->setPen(QPen(Qt::red)); m_line->point1->setCoords(0, valueAxis->range().lower); m_line->point2->setCoords(0, valueAxis->range().upper); m_label = new QCPItemText(plot); m_label->setPositionAlignment(Qt::AlignTop|Qt::AlignHCenter); m_label->position->setParentAnchor(m_line->point1); m_label->setText("Cursor"); } void Cursor::setPosition(double x) { m_x = x; m_line->point1->setCoords(x, valueAxis->range().lower); m_line->point2->setCoords(x, valueAxis->range().upper); updateLabel(); } void Cursor::updateLabel() { // 获取当前X位置对应的Y值(假设只有一个形) QCPGraph *graph = m_plot->graph(0); double y = graph->data()->at(m_x)->value; // 注意:这里需要根据实际数据结构调整 m_label->setText(QString("(%1, %2)").arg(m_x).arg(y)); } ``` ### 7. 注意事项 - **数据获取**:上述代码中,我们假设数据是连续且已排序的,并且通过`graph->data()->at(x)`直接获取。实际中可能需要使用`graph->data()->findBegin(x)`来查找最近的数据点。 - **性能优化**:频繁的鼠标移动事件可能导致重绘过多,可以考虑使用定时器进行节流。 - **多形支持**:如果有多条形,需要确定从哪条形获取Y值。可以在光标类中增加形索引成员。 ### 8. 双光标测量效果 用户通过鼠标点击切换当前操作的光标,移动鼠标时当前光标会跟随移动,并实时更新两个光标之间的时间差(Δt)和电压差(ΔV)。差值标签会显示在两点中间位置。 ### 参考文献 在双光标测量中,时间差和电压差的计算是基础功能,但实现方式需要结合具体的数据结构[^1]。 §§相关问题§§ 1. 如何提高双光标测量的精度(例如,通过数据插值)? 2. 如何在多通道波形中为每个通道独立设置光标? 3. 如何保存和加载光标测量结果? 4. 能否实现光标位置的键盘微调功能? 5. 如何实现光标位置的拖拽调整?</think># QCustomPlot双光标测量实现方法 在QCustomPlot中实现双光标测量功能,可以精确计算两点之间的时间差(Δt)和电压差(ΔV)。以下是完整的实现方案: ## 实现原理 双光标测量通过在波形上放置两个可移动的垂直光标线,计算它们在X轴(时间)和Y轴(电压)上的差值: - **时间差**:$Δt = |x_2 - x_1|$ - **电压差**:$ΔV = |y_2 - y_1|$ 其中$(x_1, y_1)$和$(x_2, y_2)$分别是两个光标的坐标。 ## 实现步骤 ### 1. 定义光标管理类 ```cpp class CursorManager : public QObject { Q_OBJECT public: explicit CursorManager(QCustomPlot *plot); // 光标操作 void addCursor(double x); void moveCursor(int index, double x); void removeCursor(int index); // 测量计算 QPair<double, double> calculateDeltas() const; // 光标位置 QVector<double> cursorPositions() const { return m_positions; } signals: void cursorsChanged(); private: QCustomPlot *m_plot; QVector<double> m_positions; // 光标位置 QVector<QCPItemStraightLine*> m_lines; // 光标线对象 }; ``` ### 2. 实现光标绘制移动 ```cpp CursorManager::CursorManager(QCustomPlot *plot) : m_plot(plot) {} void CursorManager::addCursor(double x) { if(m_positions.size() >= 2) return; // 最多两个光标 // 创建光标线 QCPItemStraightLine *cursor = new QCPItemStraightLine(m_plot); cursor->setPen(QPen(Qt::red, 1, Qt::DashLine)); cursor->point1->setCoords(x, m_plot->yAxis->range().lower); cursor->point2->setCoords(x, m_plot->yAxis->range().upper); m_lines.append(cursor); m_positions.append(x); emit cursorsChanged(); } void CursorManager::moveCursor(int index, double x) { if(index < 0 || index >= m_positions.size()) return; m_positions[index] = x; m_lines[index]->point1->setCoords(x, m_plot->yAxis->range().lower); m_lines[index]->point2->setCoords(x, m_plot->yAxis->range().upper); emit cursorsChanged(); } QPair<double, double> CursorManager::calculateDeltas() const { if(m_positions.size() < 2) return {0, 0}; // 获取两个光标位置的数据点 QCPGraph *graph = m_plot->graph(0); int index1 = graph->findBegin(m_positions[0]); int index2 = graph->findBegin(m_positions[1]); // 计算时间差和电压差 double timeDelta = qAbs(m_positions[1] - m_positions[0]); double voltageDelta = qAbs(graph->dataMainValue(index2) - graph->dataMainValue(index1)); return {timeDelta, voltageDelta}; } ``` ### 3. 主窗口集成事件处理 ```cpp class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); private slots: void onMousePress(QMouseEvent *event); void onMouseMove(QMouseEvent *event); void updateCursorDisplay(); private: QCustomPlot *m_plot; CursorManager *m_cursorManager; QLabel *m_deltaLabel; int m_currentCursorIndex = -1; // 当前操作的光标索引 }; ``` ```cpp MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { // 初始化UI m_plot = new QCustomPlot(this); setCentralWidget(m_plot); // 添加示例数据 QVector<double> x(101), y(101); for(int i=0; i<101; ++i) { x[i] = i/10.0; // 0 到 10 y[i] = qSin(x[i]); // 正弦波 } m_plot->addGraph(); m_plot->graph(0)->setData(x, y); m_plot->rescaleAxes(); // 初始化光标管理器 m_cursorManager = new CursorManager(m_plot); // 添加差值显示标签 m_deltaLabel = new QLabel("Δt: 0.0, ΔV: 0.0", this); statusBar()->addWidget(m_deltaLabel); // 连接信号 connect(m_plot, &QCustomPlot::mousePress, this, &MainWindow::onMousePress); connect(m_plot, &QCustomPlot::mouseMove, this, &MainWindow::onMouseMove); connect(m_cursorManager, &CursorManager::cursorsChanged, this, &MainWindow::updateCursorDisplay); } void MainWindow::onMousePress(QMouseEvent *event) { if(event->button() == Qt::LeftButton) { double x = m_plot->xAxis->pixelToCoord(event->pos().x()); if(m_cursorManager->cursorPositions().size() < 2) { // 添加新光标 m_cursorManager->addCursor(x); m_currentCursorIndex = m_cursorManager->cursorPositions().size() - 1; } else { // 选择最近的光标 QVector<double> positions = m_cursorManager->cursorPositions(); double dist1 = qAbs(x - positions[0]); double dist2 = qAbs(x - positions[1]); m_currentCursorIndex = (dist1 < dist2) ? 0 : 1; } } } void MainWindow::onMouseMove(QMouseEvent *event) { if(event->buttons() & Qt::LeftButton && m_currentCursorIndex >= 0) { double x = m_plot->xAxis->pixelToCoord(event->pos().x()); m_cursorManager->moveCursor(m_currentCursorIndex, x); m_plot->replot(); } } void MainWindow::updateCursorDisplay() { auto deltas = m_cursorManager->calculateDeltas(); m_deltaLabel->setText( QString("Δt: %1, ΔV: %2").arg(deltas.first, 0, 'f', 3).arg(deltas.second, 0, 'f', 3) ); } ``` ## 高级功能扩展 ### 1. 光标位置精确调整 添加键盘控制功能,实现光标位置的微调: ```cpp void MainWindow::keyPressEvent(QKeyEvent *event) { if(m_currentCursorIndex < 0) return; double step = 0.01; // 调整步长 double currentX = m_cursorManager->cursorPositions()[m_currentCursorIndex]; switch(event->key()) { case Qt::Key_Left: m_cursorManager->moveCursor(m_currentCursorIndex, currentX - step); break; case Qt::Key_Right: m_cursorManager->moveCursor(m_currentCursorIndex, currentX + step); break; default: QMainWindow::keyPressEvent(event); } m_plot->replot(); } ``` ### 2. 差值方向指示 在差值显示中添加方向指示,明确两点之间的变化趋势: ```diff QPair<double, double> CursorManager::calculateDeltas() const { if(m_positions.size() < 2) return {0, 0}; QCPGraph *graph = m_plot->graph(0); int index1 = graph->findBegin(m_positions[0]); int index2 = graph->findBegin(m_positions[1]); - double timeDelta = qAbs(m_positions[1] - m_positions[0]); - double voltageDelta = qAbs(graph->dataMainValue(index2) - - graph->dataMainValue(index1)); + double timeDelta = m_positions[1] - m_positions[0]; + double voltageDelta = graph->dataMainValue(index2) - + graph->dataMainValue(index1); return {timeDelta, voltageDelta}; } void MainWindow::updateCursorDisplay() { auto deltas = m_cursorManager->calculateDeltas(); + QString timeDir = (deltas.first >= 0) ? "→" : "←"; + QString voltDir = (deltas.second >= 0) ? "↑" : "↓"; m_deltaLabel->setText( - QString("Δt: %1, ΔV: %2").arg(qAbs(deltas.first), 0, 'f', 3).arg(qAbs(deltas.second), 0, 'f', 3) + QString("Δt: %1%3 (%2) | ΔV: %4%6 (%5)") + .arg(qAbs(deltas.first), 0, 'f', 3) + .arg(deltas.first, 0, 'f', 3) + .arg(timeDir) + .arg(qAbs(deltas.second), 0, 'f', 3) + .arg(deltas.second, 0, 'f', 3) + .arg(voltDir) ); } ``` ### 3. 数据插值提高精度 当光标位置不正好在数据点上时,使用线性插值提高测量精度: ```cpp double CursorManager::interpolatedY(double x) const { QCPGraph *graph = m_plot->graph(0); QCPGraphDataContainer::const_iterator it = graph->data()->findBegin(x); if(it == graph->data()->constEnd() || it == graph->data()->constBegin()) return 0.0; QCPGraphDataContainer::const_iterator prev = it - 1; double x0 = prev->key; double y0 = prev->value; double x1 = it->key; double y1 = it->value; // 线性插值公式: y = y0 + (y1 - y0) * (x - x0) / (x1 - x0) return y0 + (y1 - y0) * (x - x0) / (x1 - x0); } QPair<double, double> CursorManager::calculateDeltas() const { if(m_positions.size() < 2) return {0, 0}; double x1 = m_positions[0]; double x2 = m_positions[1]; double y1 = interpolatedY(x1); double y2 = interpolatedY(x2); double timeDelta = x2 - x1; double voltageDelta = y2 - y1; return {timeDelta, voltageDelta}; } ``` ## 应用场景 这种双光标测量技术广泛应用于: 1. 电子测量仪器中信号参数的精确测量[^1] 2. 医学像处理中的距离和密度分析 3. 机械振动分析中的相位差计算 4. 音频处理中的时间延迟测量 5. 金融数据分析中的价格波动评估
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值