QCustomePlot是一个可跨平台应用的开源库,可以创建自定义的Plot;而且集成简单,只需要在项目中加入头文件qcustomplot.h和qcustomplot.cpp文件,然后使一个widget提升为QCustomPlot类,即可使用。
本实例应用是使用此开源库创建带游标测量的Plot.
先上效果图
创建一个自定义plot类,继承自QCustomPlot.
#ifndef MULTICURVESPLOT_H
#define MULTICURVESPLOT_H
#include <QObject>
#include "qcustomplot.h"
#include <QMouseEvent>
#include <QMap>
#include "qDebug.h"
typedef struct _CurveData
{
QVector<double> keyVec;//x
QVector<double> valVec;//y
}CurveData;//单个曲线的数据
/*
注意:调用任何函数前,必须先已经通过init函数初始化过支持的曲线数目,送入数据源的指针
为什么不直接在构造中
*/
class MultiCurvesPlot : public QCustomPlot
{
Q_OBJECT
public:
/*传入的_allCurvesData必须是有size的*/
MultiCurvesPlot(int _curvesCnt, QWidget *parent = 0);
~MultiCurvesPlot(){qDebug() << "delete MultiCurvesPlot";}
void setCurvesName(QVector<QString> _nameVec);
void addData(int curveIdx, double x, double y);
void setAutoScroll(bool enable){autoScroll = enable;}
void setDiffSolveEnable(bool enable);
void setColors(QVector<QColor> _colors);
void setMainPlot(QCustomPlot *_mainPlot);
void setTracerEnable(bool enable);//是否使能游标
void setScatterPointEnable(bool enable);//是否使能散点
const QVector<CurveData> *getAllData(void) const;
QString getCurveName(int idx);
public slots:
void setCurveName(int idx, const QString newName);
void showCurves(QList<uint16_t> _idxList);
void storeAxisScope(bool x, bool y);//保存轴的范围
void resumeAxisScope(bool x, bool y);//恢复轴的范围
signals:
void noniusValueChanged(int _curveIdx, QString val);//游标值变化了
protected:
void timerEvent(QTimerEvent *event) Q_DECL_OVERRIDE;
virtual void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
virtual void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
virtual void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
// virtual void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
// virtual void keyReleaseEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
private slots:
void clearAllData();
void when_selectionChangedByUser();
//void when_itemDoubleClick (QCPAbstractItem *item, QMouseEvent *event){qDebug() << item;}
void when_legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event);
private:
//QCPGraph* getGraph(int curveIdx);
MultiCurvesPlot();
QVector<CurveData> allCurvesData;//所有曲线的数据,allCurvesData[n]中又含有两个QVector,也即第n条曲线的x、y值
int curvesCnt;//支持的曲线数目
QMap<int, QCPGraph*> curveIdx2graphPtr;//记录或查询:从曲线索引(key)->graph指针(val)。读之前必须查询key的存在性contains(key)?
QMap<QCPGraph*, int> graphPtr2curveIdx;//记录或查询:从graph指针(key)->曲线索引(val)。读之前必须查询key的存在性contains(key)?
QVector<QColor> getColor;//记录所有曲线的颜色
QVector<QString> getName; //记录所有曲线的名称(图例)
QList<uint16_t> idxList;//当前正在显示的曲线的编号
bool autoScroll;
QCPItemText *textLabel;//单击时提示信息框
QCPItemLine *arrow;//提示信息的箭头
QAction *actClearDatas;//右键菜单:清空历史数据
QAction *actshowAllGraph;//右键菜单:全显
/*游标相关组件*/
QCPItemTracer *tracer;//游标
QCPGraph *traceGraph;//游标要吸附哪个graph
QCPItemText *tracerXText;//用于实时显示游标X值
QCPItemText *tracerYText;//用于实时显示游标Y值
QCPItemLine *tracerArrow;
bool tracerEnable;//是否使能游标
/*两点求差组件*/
bool diffSolveEnable;//是否使能两点求差
QCPItemText *diffText;//单击时提示信息框
QPointF *lastPoint;
void solveDifference(QPointF newPoint);
/*记忆轴的范围组件*/
QCPRange Xscope;//记录X轴的范围
QCPRange Yscope;//记录Y轴的范围
};
#endif // MULTICURVESPLOT_H
部分实现如下:
#include "multicurvesplot.h"
#include "qDebug.h"
#include <QColorDialog>
#pragma execution_character_set("utf-8")
MultiCurvesPlot::MultiCurvesPlot(int _curvesCnt, QWidget *parent):
QCustomPlot(parent)
,allCurvesData(_curvesCnt)
,curvesCnt(allCurvesData.size())
,autoScroll(true)
,tracer(new QCPItemTracer(this))
,tracerEnable(false)
,lastPoint(new QPointF(0, 0))
{
this->setInteractions(QCP::iRangeDrag //可平移
| QCP::iRangeZoom //可滚轮缩放
// | QCP::iSelectPlottables //可选中曲线
| QCP::iSelectLegend );//可选中图例
this->setNoAntialiasingOnDrag(true);//禁用抗锯齿,以提高性能
connect(this, SIGNAL(selectionChangedByUser()), this, SLOT(when_selectionChangedByUser()));
connect(this, SIGNAL(legendDoubleClick(QCPLegend*, QCPAbstractLegendItem*, QMouseEvent*)), this, SLOT(when_legendDoubleClick(QCPLegend*, QCPAbstractLegendItem*, QMouseEvent*)));
this->legend->setVisible(true);//使能图例
this->axisRect()->insetLayout()->setInsetAlignment(0,Qt::AlignTop|Qt::AlignLeft);//设置图例的位置
//this->setOpenGl(true);
qDebug() << "QCustomplot opengl status = " << this->openGl();
this->yAxis2->setVisible(true);
connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));//左右y轴同步放缩
QSharedPointer<QCPAxisTickerDateTime> dateTicker(new QCPAxisTickerDateTime);//日期做X轴
dateTicker->setDateTimeFormat("hh:mm:ss.zzz\nyyyy-MM-dd");//日期格式(可参考QDateTime::fromString()函数)
//QDateTime::fromString("");
this->xAxis->setTicker(dateTicker);//设置X轴为时间轴
this->xAxis->setTickLabels(true);//显示刻度标签
/*显示数值的提示框*/
textLabel = new QCPItemText(this);
textLabel->setPositionAlignment(Qt::AlignTop|Qt::AlignHCenter);//文本框的原点位置
textLabel->position->setType(QCPItemPosition::ptAxisRectRatio);//位置类型(当前轴范围的比例)
textLabel->position->setCoords(0.5, 0); // place position at center/top of axis rect
textLabel->setText("Text Item Demo");
textLabel->setFont(QFont(font().family(), 16)); // make font a bit larger
textLabel->setPen(QPen(Qt::black)); // show black border around text
textLabel->setPadding(QMargins(2,2,2,2));//边界宽度
textLabel->setBackgroundColor(Qt::green);
//单击时指向数值的箭头:
arrow = new QCPItemLine(this);
arrow->start->setParentAnchor(textLabel->bottom);
//arrow->end->setCoords(4, 1.6); // point to (4, 1.6) in x-y-plot coordinates
arrow->setHead(QCPLineEnding::esSpikeArrow);
textLabel->setVisible(false);
arrow->setVisible(false);
//游标以及游标文本框设置
this->setMouseTracking(true);
tracer->setInterpolating(false);//禁用插值
tracer->setPen(QPen(Qt::DashLine));//虚线游标
tracer->setStyle(QCPItemTracer::tsCrosshair);//游标样式:十字星、圆圈、方框等
//游标的X值文本框
tracerXText = new QCPItemText(this);
tracerXText->setPositionAlignment(Qt::AlignBottom|Qt::AlignRight);
tracerXText->position->setType(QCPItemPosition::ptAxisRectRatio);//位置类型(当前轴范围的比例)
tracerXText->position->setCoords(0, 0.99); // 在plot中的位置
tracerXText->position->setParentAnchorX(tracer->position);//X位置锚定到游标的X位置
tracerXText->setText("no curves..");
tracerXText->setFont(QFont(font().family(), 12));
tracerXText->setPen(QPen(Qt::black));
tracerXText->setBackgroundColor(Qt::green);
tracerXText->setPadding(QMargins(2,2,2,2));//边界宽度
tracerXText->setVisible(false);
//游标的Y值文本框
tracerYText = new QCPItemText(this);
tracerYText->setPositionAlignment(Qt::AlignTop|Qt::AlignLeft);
tracerYText->position->setType(QCPItemPosition::ptAxisRectRatio);//位置类型(当前轴范围的比例)
tracerYText->position->setCoords(0.01, 0); // 在plot中的位置
tracerYText->setText("no curves..");
tracerYText->position->setParentAnchorY(tracer->position);//Y位置锚定到游标的Y位置
tracerYText->setFont(QFont(font().family(), 12));
tracerYText->setPen(QPen(Qt::black));
tracerYText->setBackgroundColor(Qt::green);
tracerYText->setPadding(QMargins(2,2,2,2));//边界宽度
tracerYText->setVisible(false);
/*两点差值的显示框*/
diffText = new QCPItemText(this);
diffText->setPositionAlignment(Qt::AlignTop|Qt::AlignRight);
diffText->setTextAlignment(Qt::AlignTop|Qt::AlignLeft);//文本框的原点位置
diffText->position->setType(QCPItemPosition::ptAxisRectRatio);//位置类型(当前轴范围的比例)
diffText->position->setCoords(0.98, 0); // place position at center/top of axis rect
diffText->setText("dt=0\r\ndy=0");
diffText->setFont(QFont(font().family(), 16)); // make font a bit larger
diffText->setPen(QPen(Qt::black)); // show black border around text
diffText->setPadding(QMargins(2,2,2,2));//边界宽度
diffText->setBackgroundColor(Qt::green);
diffText->setVisible(false);
//右键菜单
this->setContextMenuPolicy(Qt::ActionsContextMenu);
//菜单-按钮1
actshowAllGraph = new QAction("全显");
connect(actshowAllGraph, SIGNAL(triggered(bool)), this, SLOT(showAllGraph()));
this->addAction(actshowAllGraph);
//菜单-按钮2
actClearDatas = new QAction("清空历史数据");
connect(actClearDatas, SIGNAL(triggered(bool)), this, SLOT(clearAllData()));
this->addAction(actClearDatas);
getColor.resize(curvesCnt);
for(int i = 0; i < getColor.size();i++)
{
getColor[i] = QColor(qrand()%255, qrand()%255, qrand()%255);//生成随机颜色
}
getName.resize(curvesCnt);
for(int i = 0; i < getName.size();i++)//设置默认曲线名称
{
getName[i] = QString("curve %1").arg(i);
}
for(int i = 0; i < allCurvesData.size();i++)//清空备份的数据源
{
allCurvesData[i].keyVec.clear();
allCurvesData[i].valVec.clear();
}
startTimer(30, Qt::CoarseTimer);//定频刷新曲线图
}
void MultiCurvesPlot::showCurves(QList<uint16_t> _idxList)
{
// qDebug() << "MultiCurvesPlot::showCurves";
// qDebug() << "MultiCurvesPlot::showCurves:" << idxList;
this->clearGraphs();//先移除所有的graph
idxList = _idxList;//备份,可能别的函数需要它
curveIdx2graphPtr.clear();//清除曲线编号->graph指针的映射
graphPtr2curveIdx.clear();//清除graph指针->曲线编号的映射
int graphIdx = 0;
for(QList<uint16_t>::const_iterator it = idxList.begin(); it != idxList.end(); it++)
{ //*it为每一个要显示的曲线编号
uint16_t curveIdx = *it;
if(curveIdx > allCurvesData.size())
{
qDebug() << QString("warning: MultiCurvesPlot::showCurves->超出数据源max index").arg(*it);
continue;
}
this->addGraph(this->xAxis, this->yAxis);
QCPGraph* pGraph = graph(graphIdx);
curveIdx2graphPtr[curveIdx] = pGraph;//记录:曲线索引->graph指针的映射
graphPtr2curveIdx[pGraph] = curveIdx;//记录:graph指针->曲线索引的映射
pGraph->setData(allCurvesData[curveIdx].keyVec, allCurvesData[curveIdx].valVec, true);//数据源 (todo:*it的合法性)
pGraph->setPen(QPen(getColor[curveIdx]));//线的颜色随机
pGraph->setLineStyle(QCPGraph::lsStepLeft);//阶梯线样式
//this->graph(graphIdx)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 3));//显示散点
this->graph(graphIdx)->setName(getName[curveIdx]);
graphIdx++;
}
if(graphCount() > 0)
traceGraph = graph(0);
else
traceGraph = NULL;
// showAllGraph();
this->replot();
}
在窗口类中调用MultiCurvesPlot,
#include "mainwindow.h"
#include "ui_mainwindow.h"
#define CURVE_CNT 5 //最多显示几条曲线
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
plot = new MultiCurvesPlot(CURVE_CNT, this);
plot->setGeometry(20, 170, 800, 350);//设置位置和大小(但不推荐这么做,应该放进布局中使之能自动调整大小)
plot->yAxis->setRange(-1.5, 1.5);//设置Y轴的范围
QList<uint16_t> list;
list << 0 << 1 << 3;
plot->showCurves(list);//显示0号、1号、3号曲线
this->startTimer(20);//启动定时器,用于生成模拟点
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::timerEvent(QTimerEvent *event)
{
Q_UNUSED(event);
double t = (double)(QDateTime::currentMSecsSinceEpoch()) / 1000.0;//当前时间
plot->addData(0, t, qSin(t));//曲线0,sin曲线
plot->addData(1, t, qCos(t));//曲线1,cos曲线
plot->addData(2, t, 0.5 * qSin(t));//曲线2,sin
plot->addData(3, t, 0.5 * qCos(t));//曲线3,cos
plot->addData(4, t, 1);//曲线4,水平直线
}
void MainWindow::on_checkBox_tracer_clicked(bool checked)
{
plot->setTracerEnable(checked);
}
void MainWindow::on_checkBox_scroll_clicked(bool checked)
{
plot->setAutoScroll(checked);
}