QCustomPlot 曲线数据结构与存取

对了,我开通了微信公众号,计划是两边会同步更新,并逐步的会将博客上的文章同步至公众号中。感兴趣的朋友可以搜索“里先森sements”来关注,欢迎来玩~!


通常,我们对QCustomPlot中的曲线数据无外乎增、删、改、查这几种操作。然而对于初次或者接触QCustomPlot时间不长的用户来说,其提供的曲线数据操作接口可能让人有些摸不着头脑。因此,在这里记录本人对QCustomPlot曲线数据操作的一些理解和经验,以供各位参考。这里我们仅讨论常用的QCPGraph数据,QCPCurve不在讨论范围内。

对于大部分用户来说,使用QCustomPlot是为了展示序列化的曲线数据,即每一个键(key,x轴坐标)仅对应一个值(value,y轴坐标),并且这些数据往往是已经按照顺序排列好的。然而,QCustomPlot为了兼容多种应用(如单个键对应多个值),其内部的设计要更为复杂。下面的例子展示了单个键对应多个值的情况

ui->CP_Plot->addGraph();
auto graph =ui->CP_Plot->graph();
graph->addData(1,1.1);
graph->addData(1,2.1);
graph->addData(2,1.2);
graph->addData(2,2.2);
graph->addData(3,3.5);

在这里插入图片描述

为了实现对曲线数据的安全高效管理,QCustomPlot实现了一个数据存储容器:QCPDataContainer。这里T是模板。我们常见的便是 QCPDataContainer,你可以通过下面的方法来获取它

//获取新建立图层的数据存储容器
ui->CP_Plot->addGraph();		
QCPDataContainer<QCPGraphData>* pContainer = ui->CP_Plot->graph()->data().data();

注意,

g

r

a

p

h

(

)

?

>

d

a

t

a

(

)

graph()->data()

graph()?>data() 返回的是 QSharedPointer 智能指针,你需要调用其

d

a

t

a

(

)

data()

data() 方法才能获取

Q

C

P

D

a

t

a

C

o

n

t

a

i

n

e

r

<

Q

C

P

G

r

a

p

h

D

a

t

a

>

?

QCPDataContainer<QCPGraphData>*

QCPDataContainer<QCPGraphData>?。作为数据存储容器,QCPDataContainer 实现了很多常见的数据操作,部分操作可以直接通过调用方法执行,而部分则需要使用其内部的数据迭代器(iterator)。

绝大部分情况下,用户希望对已知键位置的数据点进行操作。然而正如前文所述,由于QCustomPlot的数据存储容器需要考虑一个键对应多个值的情况,因此你所持有的键并不能完全确定你所需要定位的点(例如前例中的点(1,2.1)与点(1,1.1))。在查看 QCustomPlot 的帮助文档时,你也会发现部分方法需要提供的参数并非键(key)而是数据下标(index)。这是由于 QCPDataContainer 内的数据都是以顺序方式存储的,索引每个数据点的并非键,而是其顺序下标。在 QCPDataContainer 的帮助文档中你可以看到下面的表述:

The data is stored in a sorted fashion, which allows very quick lookups by the sorted key as well as retrieval of ranges (see findBegin, findEnd, keyRange) using binary search. The container uses a preallocation and a postallocation scheme, such that appending and prepending data (with respect to the sort key) is very fast and minimizes reallocations. If data is added which needs to be inserted between existing keys, the merge usually can be done quickly too, using the fact that existing data is always sorted.

因此,你可以理解为我们在绘图坐标系上使用到的键(key,x轴坐标)与值(value,y轴坐标)都不过是数据存储容器中某个元素内部的值而已,真正要找到这个元素,要么通过元素下标,要么枚举查找。理解了这一点,便能把握整体思路与方向。下面我们以常见的,已知数据点键(key),介绍几种对曲线中对应数据点进行操作的方法。

3.2.1 数据的增加与删除

数据的增加与删除比较简单,QCPDataContainer 已经给出了对应的方法,按需调用即可:

/*** 增加点(4,4.1) ***/
//方法1,使用 QCPDataContainer 的 add() 方法
ui->CP_Plot->graph()->data()->add(QCPGraphData(4,4.1));		//QSharedPoint指针
//方法2,使用 QCPGraph 的 addData() 方法
ui->CP_Plot->graph()->addData(4,4.1);

/*** 删除点 ***/
ui->CP_Plot->graph()->data()->remove(4);	//删除 键key==4的所有点
ui->CP_Plot->graph()->data()->clear();		//删除曲线内所有数据点

其余类似方法(如 removeBefore)可参考相关文档。注意,QCPDataContainer 的

r

e

m

o

v

e

(

d

o

u

b

l

e

s

o

r

t

K

e

y

)

remove(double sortKey)

remove(doublesortKey) 方法会移除所有键相同的点,如若仅需移除同键的部分点,需结合枚举查值的方式进行。

3.2.2 数据值查找

在查看某键对应的数据点的值时,需要用到数据存储容器的迭代器与 QCPDataContainer 的方法

f

i

n

d

B

e

g

i

n

(

)

findBegin()

findBegin()。该方法在 QCustomPlot 内部实质上也是枚举所有的数据点,逐个比对其键的值,而后根据参数返回用户需求的数据点的迭代器。

ui->CP_Plot->addGraph();

/*** 新增数据点 ***/
auto graph =ui->CP_Plot->graph();
graph->addData(1,1.1);
graph->addData(1,2.1);
graph->addData(2,1.2);
graph->addData(2,2.2);
graph->addData(3,3.1);

/*** 使用迭代器查看数据点值 ***/
//获取key为1的数据点迭代器,该例中其指向第一个数据点(1,1.1)
QCPDataContainer<QCPGraphData>::const_iterator it = graph->data()->findBegin(1,false);
//枚举所有数据点直至QCPDataContainer末尾
for(;it != graph->data()->constEnd(); it++)
{
    qDebug()<<"it->sortKey(): "<<it->sortKey();
    qDebug()<<"it->mainKey()/it->key: "<<it->mainKey();
    qDebug()<<"it->mainValue()/it->value: "<<it->mainValue()<<"
";
}

f

i

n

d

B

e

g

i

n

(

)

findBegin()

findBegin() 方法的 expandedRange 参数可以限定搜索方法。具体请参照 QCustomPlot 说明文档

If expandedRange is true, the data point just below sortKey will be considered, otherwise the one just above.
This can be used in conjunction with findEnd to iterate over data points within a given key range, including or excluding the bounding data points that are just beyond the specified range.
If expandedRange is true but there are no data points below sortKey, constBegin is returned.

3.2.3 数据值修改

修改数据点的值时同样需要使用迭代器,但这里使用的迭代器是非常量迭代器QCPDataContainer::iterator,该迭代器允许我们直接修改元素的相关变量,如键(key)与值(value)。

/*
 *  修改已有数据点值
 *  例 将第5个数据点的值从3修改为3.5
 */
QCPDataContainer<QCPGraphData>::iterator mit;
//方法1 直接定位,需知道数据点顺序
mit = graph->data()->begin() + 4;
mit->value = 3.5;

//方法2 枚举key值查找
mit = graph->data()->begin();
for(; mit != graph->data()->end(); mit++)
{
    if(3 == mit->key)
        break;
}
mit->value = 3.5;

//方法3 remove and add
it = graph->data()->findBegin(3,false);
QCPGraphData newData(it->key,3.5);
graph->data()->remove(3);               //移除key为3的所有数据点
graph->data()->add(newData);