本篇为官方例子的解读:绘图截图:高级轴演示 Plot Screenshots: Advanced Axes Demo
推荐其他人总结的笔记:qcustomplot使用教程–基本绘图
1.删除原有的默认布局
customPlot->plotLayout()->clear(); // 清除默认的rect轴,以便我们可以从头开始
customPlot->replot(); // 重绘
- 原先默认有一个(0,5)范围的坐标轴布局, 执行代码后直接清空变全白了.
2.插入坐标轴
customPlot->plotLayout()->clear(); // 清除默认的rect轴,以便我们可以从头开始
QCPAxisRect *wideAxisRect = new QCPAxisRect(customPlot); // 一个坐标轴
QCPLayoutGrid *subLayout = new QCPLayoutGrid; // 一个布局网络
customPlot->plotLayout()->addElement(0, 0, wideAxisRect); // 坐标轴放第一行
customPlot->plotLayout()->addElement(1, 0, subLayout); // 布局放第二行
QCPAxisRect *subRectLeft = new QCPAxisRect(customPlot, false); // 一个坐标轴 false表示不设置默认轴
QCPAxisRect *subRectRight = new QCPAxisRect(customPlot, false); // 一个坐标轴 false表示不设置默认轴
subRectLeft->addAxes(QCPAxis::atBottom | QCPAxis::atLeft); // 添加下面和左边的坐标轴
subRectRight->addAxes(QCPAxis::atBottom | QCPAxis::atRight); // 添加下面和右边的坐标轴
subLayout->addElement(0, 0, subRectLeft); // 坐标轴放在左边
subLayout->addElement(0, 1, subRectRight); // 坐标轴放在右边
customPlot->replot(); // 重绘
- 可以看到第一行上面的坐标是默认带网格的.下面第二行两个坐标轴就算只有坐标轴的;
3.重叠坐标轴
// # 3.重叠坐标轴
wideAxisRect->setupFullAxesBox(true); // 设置右和上坐标轴的可见性,并将左和下坐标轴的属性给予右和上坐标轴, 注意,这一步执行完后右和上坐标轴是没有数值的,只有刻度
wideAxisRect->axis(QCPAxis::atRight, 0)->setTickLabels(true); // 第一个函数是获取右边索引第一个的坐标轴, 然后使其显示数字
wideAxisRect->addAxis(QCPAxis::atLeft)->setTickLabelColor(QColor("#6050F8")); // 在左边(再)添加一个坐标轴,并且设置数字的颜色
- 注意其中用到的添加重叠坐标轴的函数
addAxis()
,和索引坐标轴的函数axis()
; - 右和上坐标轴的范围被赋予和左下坐标轴一样;
4.坐标轴尺寸与刻度
// # 4.坐标轴尺寸与刻度
subRectRight->setMaximumSize(150, 150); // 设置最大尺寸
subRectRight->setMinimumSize(150, 150); // 设置最小尺寸, 结合相当于是固定尺寸
subRectLeft->axis(QCPAxis::atLeft)->ticker()->setTickCount(2); // 左坐标轴范围不变, 主刻度只有2个
subRectRight->axis(QCPAxis::atRight)->ticker()->setTickCount(2); // 右坐标轴范围不变, 主刻度只有2个
subRectRight->axis(QCPAxis::atBottom)->ticker()->setTickCount(2); // 下坐标轴范围不变, 主刻度只有2个
subRectLeft->axis(QCPAxis::atBottom)->grid()->setVisible(true); // 下坐标轴的网格线显示可见
- 注意,设置坐标轴尺寸后,会自动填满;而且下面2个坐标轴的左右已经和上面1个坐标轴不对齐了;
- 注意,要设置坐标轴刻度相关的,先获取坐标轴
axis()
再获取刻度ticker()
, - 类似的,要设置网格线相关的,先获取坐标轴
axis()
再获取刻度grid()
,
5.归类分组
// 同步顶部和底部轴矩形的左右边距:
QCPMarginGroup *marginGroup = new QCPMarginGroup(customPlot); // 创建一个用于同步关联的对象
subRectLeft->setMarginGroup(QCP::msLeft, marginGroup); // 丢入三个坐标轴中
subRectRight->setMarginGroup(QCP::msRight, marginGroup); // 丢入三个坐标轴中
wideAxisRect->setMarginGroup(QCP::msLeft | QCP::msRight, marginGroup); // 丢入三个坐标轴中
//移动“轴”层上新创建的轴和“网格”层上的网格:
foreach (QCPAxisRect *rect, customPlot->axisRects()) // 获取所有坐标轴
{
foreach (QCPAxis *axis, rect->axes()) // 获取所有x和y坐标轴
{
axis->setLayer("axes"); // 将坐标轴归类分组
axis->grid()->setLayer("grid"); // 将所有网格线归类分组
}
}
// 生成数据, 留着备用
QVector<QCPGraphData> dataCos(21), dataGauss(50), dataRandom(100);
QVector<double> x3, y3;
qsrand(3);
for (int i=0; i<dataCos.size(); ++i)
{
dataCos[i].key = i/static_cast<double>(dataCos.size()-1)*10-5.0;
dataCos[i].value = qCos(dataCos[i].key);
}
for (int i=0; i<dataGauss.size(); ++i)
{
dataGauss[i].key = i/static_cast<double>(dataGauss.size())*10-5.0;
dataGauss[i].value = qExp(-dataGauss[i].key*dataGauss[i].key*0.2)*1000;
}
for (int i=0; i<dataRandom.size(); ++i)
{
dataRandom[i].key = i/static_cast<double>(dataRandom.size())*10;
dataRandom[i].value = qrand()/static_cast<double>(RAND_MAX)-0.5+dataRandom[qMax(0, i-1)].value;
}
x3 << 1 << 2 << 3 << 4;
y3 << 2 << 2.5 << 4 << 1.5;
- 对比上一个,下面2个坐标轴已经和上面1个坐标轴的左右对齐了;
- 这个对齐和分组的方法有点难以理解,暂时死记好了.
6.画2个折线图
// # 6.画2个折线图
QCPGraph *mainGraphCos = customPlot->addGraph(wideAxisRect->axis(QCPAxis::atBottom), wideAxisRect->axis(QCPAxis::atLeft)); // 添加一条折线图,使用下面第一个和左边第一个坐标轴
mainGraphCos->data()->set(dataCos); // 设置数据
mainGraphCos->valueAxis()->setRange(-1, 1); // 设置y轴范围
mainGraphCos->rescaleKeyAxis(); // 设置x轴自适应
mainGraphCos->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, QPen(Qt::black), QBrush(Qt::white), 6)); // 设置散点样式
mainGraphCos->setPen(QPen(QColor(120, 120, 120), 2)); // 设置线颜色
QCPGraph *mainGraphGauss = customPlot->addGraph(wideAxisRect->axis(QCPAxis::atBottom), wideAxisRect->axis(QCPAxis::atLeft, 1)); // 再添加一条折线图,使用下面第一个和左边第二个坐标轴
mainGraphGauss->data()->set(dataGauss); // 设置数据
mainGraphGauss->valueAxis()->setRange(0, 1000); // 设置y轴范围
mainGraphGauss->rescaleKeyAxis(); // 设置x轴自适应
mainGraphGauss->setPen(QPen(QColor("#8070B8"), 2)); // 设置线颜色
mainGraphGauss->setBrush(QColor(110, 170, 110, 30)); // 设置和x轴的闭合填充颜色
mainGraphCos->setChannelFillGraph(mainGraphGauss); // 修改第一条折线的默认闭合空间
mainGraphCos->setBrush(QColor(255, 161, 0, 50)); // 设置和的第二条折线的闭合填充颜色, 注意, 闭合空间有重叠的部分, 后配置的会覆盖前配置的
- 注意,添加折线使用函数,
addGraph()
,然后会返回句柄,使用该句柄配置折线,可以使用graph(0)
获取已经添加的折线图层; - 注意,折线默认的闭合空间是和
KeyAxis
坐标轴,可以通过函数setChannelFillGraph()
修改为特定曲线; - 注意,图像有重叠的优先级之分, 也可能是先后之分, 具体…自己实测吧.
7.画1个类似柱状图的图
// # 7.画1个类似柱状图的图
QCPGraph *subGraphRandom = customPlot->addGraph(subRectLeft->axis(QCPAxis::atBottom), subRectLeft->axis(QCPAxis::atLeft));
subGraphRandom->data()->set(dataRandom); // 设置数据
subGraphRandom->setLineStyle(QCPGraph::lsImpulse); // 设置直线类型 lsImpulse 每个数据点由一条平行于值轴的线表示,该线从数据点延伸到零值线
subGraphRandom->setPen(QPen(QColor("#FFA100"), 1.5)); // 设置线颜色
subGraphRandom->rescaleAxes(); // 设置xy轴自适应
- 注意使用函数
rescaleAxes()
一次性自适应两个轴, - 注意,纵坐标依旧只有两个主刻度;
- 这个线的类型有点像柱状图;
8.画一个柱状图
// # 8.画一个柱状图
QCPBars *subBars = new QCPBars(subRectRight->axis(QCPAxis::atBottom), subRectRight->axis(QCPAxis::atRight)); // 添加一个柱状图
subBars->setWidth(3/static_cast<double>(x3.size())); // 设置柱状图的宽度, 计算平均一点
subBars->setData(x3, y3); // 设置数据
subBars->setPen(QPen(Qt::black)); // 设置线颜色
subBars->setAntialiased(false); // 设置此对象是否绘制为反锯齿
subBars->setAntialiasedFill(false); // 设置此绘图对象的填充是否绘制为反锯齿
subBars->setBrush(QColor("#705BE8")); // 设置柱状填充颜色
subBars->keyAxis()->setSubTicks(false); // 刻度尺上的次刻度,隐藏
subBars->rescaleAxes(); // 设置xy轴自适应
// 为subBars键轴设置一个仅给出整数刻度的自动收报机:
QSharedPointer<QCPAxisTickerFixed> intTicker(new QCPAxisTickerFixed); // 创建一个刻度
intTicker->setTickStep(1.0); // 设置刻度的间距
intTicker->setScaleStrategy(QCPAxisTickerFixed::ssMultiples); // 允许指定刻度步长的整数倍
subBars->keyAxis()->setTicker(intTicker); // 将该刻度对象赋值给凸显
- 注意, 柱状图使用的是函数
QCPBars()
,不同于折线图, - 注意,柱状图一般都要关闭绘画抗锯齿;
- 注意,柱状图要设置柱子的宽度,不然会密密麻麻连成一片;
- 注意,柱状图一般要重新设置横坐标,
9.总结
- 至此分析完成;
- 上面的代码全部拼在一起就好了.