QCustomplot绘制动态曲线图

您所在的位置:网站首页 在哪设置鼠标输入跟踪功能 QCustomplot绘制动态曲线图

QCustomplot绘制动态曲线图

2024-03-03 07:46| 来源: 网络整理| 查看: 265

一.效果图

效果图如下: 在这里插入图片描述

二.具体功能

具体功能如下:

显示动态曲线及图例图例文本颜色与其对应的曲线一致右侧显示实时数据,数据点以圆点样式显示,实时数据显示无文本框曲线数值跟踪,当鼠标移动时,显示鼠标所在点的所有曲线的y值曲线图的缩放和平移 关键的点在代码中都有注释,正文就不再赘述。本功能基于QCustomplot v2.0.1版本实现,QCustomplot的使用很简单,从官网下载后,解压,将qcustomplot.h好qcustomplot.cpp拷贝到自己的project目录下,并加入到项目中即可。 需要在.pro文件中添加如下内容: QT += printsupport 三.代码结构

代码结构如下图所示:

在这里插入图片描述

PlotValueTracer:游标类,用于显示游标和数值 PlotBaseWidget:曲线图窗口,便于直接使用

四.代码

主要代码如下:

PlotValueTracer.h代码

#ifndef PLOTVALUETRACER_H #define PLOTVALUETRACER_H #include #include "qcustomplot.h" //定义追踪样式 enum PlotValueTracerType { XAxisTracer, YAxisTracer, DataTracer }; class PlotValueTracer : public QObject { Q_OBJECT public: explicit PlotValueTracer(QPointer _parentPlot, PlotValueTracerType _type,QCPGraph *_graph = 0); ~PlotValueTracer(); // setters: void setTracerStyle(QCPItemTracer::TracerStyle e); //设置游标点的样式 void setArrowLineLength(int len); //设置线元素的偏移长度 void setArrowHead(QCPLineEnding::EndingStyle e); //设置线元素头样式 void setArrowPen(const QPen &pen); //设置线元素的画笔 void setTracerPen(const QPen &pen); //设置游标点的画笔 void setLabelPen(const QPen &pen); //设置文本元素的画笔 void setTracerBrush(const QBrush &brush); //设置游标点的画刷 void setLabelBrush(const QBrush &brush); //设置文本元素画刷 void setText(const QString &text); //设置文本元素文本 void setTextColor(QColor &c); //设置文本元素中文本的颜色 void setTextMargin(QMargins mar); //设置文本元素边界 void setVisible(bool visible); //设置游标的显示/隐藏 void updateTracerPosition(double x,double y); //更新游标位置 private: void updatePositionX(double value); //XAxisTracer显示模式,更新游标位置 void updatePositionY(double value); //YAxisTracer显示模式,更新游标位置 void updatePositionData(double xValue, double yValue); //DataTracer显示模式,更新游标位置 private: QPointer m_parentPlot; //曲线图 QCPGraph *m_graph; //这里是存传入的绘图图层 QPointer m_tracer; //数据跟踪点 QPointer m_arrowLine; //线元素 QPointer m_labelText; //数据跟踪点文本元素 PlotValueTracerType m_type; //追踪点类型 int m_nLineLength; //线元素的长度 }; #endif // PLOTVALUETRACER_H

PlotValueTracer.cpp代码

#include "PlotValueTracer.h" PlotValueTracer::PlotValueTracer(QPointer _parentPlot,PlotValueTracerType _type,QCPGraph *_graph) : QObject(_parentPlot), m_parentPlot(_parentPlot), m_type(_type), m_graph(_graph), m_nLineLength(15) { //游标点 m_tracer = new QCPItemTracer(m_parentPlot); if(m_graph) { m_tracer->setPen(m_graph->pen()); m_tracer->setBrush(m_graph->brush()); } //线元素 m_arrowLine = new QCPItemLine(m_parentPlot); m_arrowLine->setLayer("overlay"); m_arrowLine->setClipToAxisRect(false); if(m_graph) m_arrowLine->setPen(m_graph->pen());//设置箭头的颜色 m_arrowLine->setHead(QCPLineEnding::esSpikeArrow); //文本元素 m_labelText = new QCPItemText(m_parentPlot); m_labelText->setLayer("overlay"); m_labelText->setClipToAxisRect(false); m_labelText->setPadding(QMargins(3, 4, 3, 4)); if(m_graph) m_labelText->setPen(m_graph->pen()); m_labelText->position->setParentAnchor(m_tracer->position);//m_arrowLine->start); switch(m_type) { case XAxisTracer: m_tracer->position->setTypeX(QCPItemPosition::ptPlotCoords); m_tracer->position->setTypeY(QCPItemPosition::ptAxisRectRatio); m_arrowLine->end->setParentAnchor(m_tracer->position); m_arrowLine->start->setParentAnchor(m_arrowLine->end); m_arrowLine->start->setCoords(m_nLineLength, 0);//偏移量,用于设置线元素的长度 m_labelText->setPositionAlignment(Qt::AlignTop|Qt::AlignHCenter); break; case YAxisTracer: m_tracer->position->setTypeX(QCPItemPosition::ptAxisRectRatio); m_tracer->position->setTypeY(QCPItemPosition::ptPlotCoords); m_tracer->position->setCoords(1, 0); //这一句必不可少 m_arrowLine->end->setParentAnchor(m_tracer->position); m_arrowLine->start->setParentAnchor(m_arrowLine->end);//m_labelText->position m_arrowLine->end m_arrowLine->start->setCoords(m_nLineLength, 0);//偏移量,用于设置线元素的长度 m_labelText->setPositionAlignment(Qt::AlignTop|Qt::AlignVCenter); break; case DataTracer: m_tracer->position->setTypeX(QCPItemPosition::ptPlotCoords); m_tracer->position->setTypeY(QCPItemPosition::ptPlotCoords); m_arrowLine->end->setParentAnchor(m_tracer->position); m_arrowLine->start->setParentAnchor(m_arrowLine->end); m_arrowLine->start->setCoords(m_nLineLength, 0);//偏移量,用于设置线元素的长度 m_labelText->setPositionAlignment(Qt::AlignLeft|Qt::AlignVCenter); break; } setVisible(false); } //析构函数 PlotValueTracer::~PlotValueTracer() { if (m_tracer) m_tracer->parentPlot()->removeItem(m_tracer); if (m_arrowLine) m_arrowLine->parentPlot()->removeItem(m_arrowLine); if (m_labelText) m_labelText->parentPlot()->removeItem(m_labelText); } //设置游标点的样式 void PlotValueTracer::setTracerStyle(QCPItemTracer::TracerStyle e) { if(m_tracer) m_tracer->setStyle(e); } //设置线元素的偏移长度 void PlotValueTracer::setArrowLineLength(int len) { m_nLineLength = len; m_arrowLine->start->setCoords(m_nLineLength, 0);//偏移量 } //设置线元素头样式 void PlotValueTracer::setArrowHead(QCPLineEnding::EndingStyle e) { if(m_arrowLine) m_arrowLine->setHead(e); } //设置线元素的画笔 void PlotValueTracer::setArrowPen(const QPen &pen) { if(m_arrowLine) m_arrowLine->setPen(pen); } //设置游标点的画笔 void PlotValueTracer::setTracerPen(const QPen &pen) { if(m_tracer) m_tracer->setPen(pen); } //设置文本元素的画笔 void PlotValueTracer::setLabelPen(const QPen &pen) { if(m_labelText) m_labelText->setPen(pen); } //设置游标点的画刷 void PlotValueTracer::setTracerBrush(const QBrush &brush) { if(m_tracer) m_tracer->setBrush(brush); } //设置文本元素画刷 void PlotValueTracer::setLabelBrush(const QBrush &brush) { if(m_labelText) m_labelText->setBrush(brush); } //设置文本元素文本 void PlotValueTracer::setText(const QString &text) { if(m_labelText) m_labelText->setText(text); } //设置文本元素中文本的颜色 void PlotValueTracer::setTextColor(QColor &c) { if(m_labelText) m_labelText->setColor(c); } //设置文本元素边界 void PlotValueTracer::setTextMargin(QMargins mar) { if(m_labelText) m_labelText->setPadding(mar); } //设置游标的显示/隐藏 void PlotValueTracer::setVisible(bool visible) { if(m_tracer) m_tracer->setVisible(visible); if(m_arrowLine) m_arrowLine->setVisible(visible); if(m_labelText) m_labelText->setVisible(visible); } //更新游标位置 void PlotValueTracer::updateTracerPosition(double x, double y) { switch(m_type) { case XAxisTracer: updatePositionX(x); break; case YAxisTracer: updatePositionY(y); break; case DataTracer: updatePositionData(x,y); break; } } //XAxisTracer显示模式,更新游标位置-UNTEST void PlotValueTracer::updatePositionX(double xValue) { setVisible(true); m_tracer->position->setCoords(xValue, 1); m_labelText->position->setCoords(0, m_nLineLength); m_arrowLine->start->setCoords(0, m_nLineLength); m_arrowLine->end->setCoords(0, 0); } //YAxisTracer显示模式,更新游标位置 void PlotValueTracer::updatePositionY(double value) { setVisible(true); // since both the arrow and the text label are chained to the dummy tracer (via anchor // parent-child relationships) it is sufficient to update the dummy tracer coordinates. The // Horizontal coordinate type was set to ptAxisRectRatio so to keep it aligned at the right side // of the axis rect, it is always kept at 1. The vertical coordinate type was set to // ptPlotCoordinates of the passed parent axis, so the vertical coordinate is set to the new // value. m_tracer->position->setCoords(1, value); // We want the arrow head to be at the same horizontal position as the axis backbone, even if // the axis has a certain offset from the axis rect border (like the added second y axis). Thus we // set the horizontal pixel position of the arrow end (head) to the axis offset (the pixel // distance to the axis rect border). This works because the parent anchor of the arrow end is // the dummy tracer, which, as described earlier, is tied to the right axis rect border. //m_arrowLine->end->setCoords(m_axis->offset(), 0); m_labelText->position->setCoords(10, 0); } //DataTracer显示模式,更新游标位置 void PlotValueTracer::updatePositionData(double xValue, double yValue) { setVisible(true); if (yValue > m_parentPlot->yAxis->range().upper) yValue = m_parentPlot->yAxis->range().upper; m_tracer->position->setCoords(xValue, yValue); m_labelText->position->setCoords(m_nLineLength, 0); }

PlotBaseWidget.h代码

#ifndef PLOTBASEWIDGET_H #define PLOTBASEWIDGET_H #include #include "qcustomplot.h" #include "PlotValueTracer.h" //曲线属性 struct StLineInfo { QString name; //曲线名称 QColor c; //曲线颜色 StLineInfo(){c = Qt::transparent;} }; class PlotBaseWidget : public QWidget { Q_OBJECT public: explicit PlotBaseWidget(QWidget *parent = 0,int w = 400,int h = 300); /** * @brief 设置曲线信息 * @param mapLineInfo 曲线信息,键值用于标识每条曲线 */ void CreateGraph(QMap mapLineInfo); /** * @brief 添加数据 * @param key 键值 * @param mapData 数据内容,键为数据类型标识,值为key对应的值 */ void AddData(double key,QMap mapData); /** * @brief 设置游标标签的显示和隐藏 * @param b 显示/隐藏 */ void ShowTagLabels(bool b); /** * @brief 设置数值游标的显示和隐藏 * @param b 显示/隐藏 */ //void ShowValueTracer(bool b); /** * @brief 设置X轴范围 * @param id 用于区别下侧X轴和上侧X轴,0-下侧,1上侧 * @param lower 小值 * @param upper 大值 */ void SetXrange(int id,double lower, double upper); /** * @brief 设置Y轴范围 * @param id 用于区别左侧Y轴和右侧Y轴,0-左侧,1右侧 * @param lower 小值 * @param upper 大值 */ void SetYrange(int id,double lower, double upper); /** * @brief 设置X轴窗口长度(动态曲线使用) * @param w 窗口长度 */ void SetXLength(int w); protected slots: void slotShowValueTracer(QMouseEvent*); //显示数值游标 private: void InitParam(); //初始化参数 QColor GetUsefullColor(int i); //获取可用颜色 private: QCustomPlot* m_plot; //曲线窗口 //定义曲线属性信息 struct StLineInfoAll { QPointer graph; //图层标识 QPointer tag; //游标 QPointer vtrac; //数值游标 StLineInfo info; //曲线信息 StLineInfoAll(){graph = 0; tag = 0;vtrac = 0;} }; QMap m_mapLineInfo; //曲线属性 QList m_listColorDef; //默认颜色 int m_nXlength; //X轴长度 int m_nYchanged; //0=表示y变化,1表示y2变化 }; #endif // PLOTBASEWIDGET_H

PlotBaseWidget.cpp代码

#include "PlotBaseWidget.h" PlotBaseWidget::PlotBaseWidget(QWidget *parent,int w,int h) : QWidget(parent) { setFixedSize(w,h); InitParam(); m_plot = new QCustomPlot(this); m_plot->setFixedSize(w,h); //x坐标轴设置 m_plot->xAxis->setLabel("TIME"); //设置坐标名字 m_plot->xAxis->setLabelColor(Qt::black); //设置坐标颜色 m_plot->xAxis->setLabelPadding(1); //设置坐标轴名称文本距离坐标轴刻度线距离 //m_plot->xAxis->setRange(0,1000); //设置X轴范围 //设置Y轴 /*说明:虽然通过setVisible()可以设置Y2轴的不可见,但是在绘制时游标标签需要重新进行设置 *因为setVisible(false)后,Y2轴不绘制,Y2轴上的刻度线长度将无用,而将画笔设为Qt::NoPen, *则是使用透明画笔绘制Y2轴,其刻度线长度仍然占用空间,也就不会将游标标签的空间压缩,导致游标 *标签显示不完整,这就需要在基础控件中修改游标标签的位置 */ m_plot->yAxis2->setTickLabels(false); //设置y轴刻度值不显示 m_plot->yAxis2->setBasePen(Qt::NoPen); //设置y2轴的绘制画笔 m_plot->yAxis2->setTickPen(Qt::NoPen); //设置y2轴的主刻度线绘制画笔 m_plot->yAxis2->setSubTickPen(Qt::NoPen); //设置y2轴的子刻度线绘制画笔 connect(m_plot->yAxis2, SIGNAL(rangeChanged(QCPRange)), m_plot->yAxis, SLOT(setRange(QCPRange))); // left axis only mirrors inner right axis m_plot->yAxis2->setVisible(true); m_plot->axisRect()->axis(QCPAxis::atRight, 0)->setPadding(55); // add some padding to have space for tags //m_plot->yAxis2->setRange(-2,2); //y轴的范围 //鼠标移动事件 connect(m_plot, SIGNAL(mouseMove(QMouseEvent*)), this,SLOT(slotShowValueTracer(QMouseEvent*))); //设置曲线可拖拽、可缩放 //、可选择 m_plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom ); //| QCP::iSelectPlottables //m_plot->axisRect()->setRangeZoom(Qt::Horizontal); //设置水平缩放 //m_plot->axisRect()->setRangeDrag(Qt::Horizontal); //设置水平拖拽 } // 设置曲线信息 void PlotBaseWidget::CreateGraph(QMap mapLineInfo) { m_mapLineInfo.clear(); //显示图例 m_plot->legend->setVisible(true); //QColor bc = m_plot->legend->brush().color(); m_plot->legend->setBrush(QBrush(QColor(255,255,255,0))); m_plot->legend->setRowSpacing(-3); // 创建图层及游标 QMap::Iterator it = mapLineInfo.begin(); for(int i = 0; it != mapLineInfo.end(); it++,i++) { StLineInfo st = it.value(); StLineInfoAll line; // 创建图层 line.graph = m_plot->addGraph(); line.graph->setName(st.name); //获取图层颜色 QColor cc; if(st.c == Qt::transparent) cc = GetUsefullColor(i); else cc = st.c; line.graph->setPen(QPen(cc)); m_plot->legend->item(i)->setTextColor(cc); //设置图例中每条线的文本颜色 // 创建右侧游标 line.tag = new PlotValueTracer(m_plot,YAxisTracer); line.tag->setArrowLineLength(5); line.tag->setArrowHead(QCPLineEnding::esDisc); line.tag->setTracerStyle(QCPItemTracer::tsNone); line.tag->setArrowPen(line.graph->pen()); line.tag->setLabelPen(Qt::NoPen); line.tag->setTextColor(cc); line.tag->setText(""); // 创建数值游标 line.vtrac = new PlotValueTracer(m_plot,DataTracer); line.vtrac->setArrowHead(QCPLineEnding::esSpikeArrow); line.vtrac->setTracerStyle(QCPItemTracer::tsNone); line.vtrac->setArrowPen(QPen(cc)); // line.vtrac->setTracerPen(QPen(cc)); line.vtrac->setLabelPen(Qt::NoPen); line.vtrac->setTextColor(cc); line.info.name = st.name; line.info.c = cc; m_mapLineInfo.insert(it.key(),line); } } // 添加数据 void PlotBaseWidget::AddData(double key,QMap mapData) { QMap::Iterator it = mapData.begin(); for(; it != mapData.end(); it++) { double vv = it.value(); QMap::Iterator ff = m_mapLineInfo.find(it.key()); if(ff != m_mapLineInfo.end()) { StLineInfoAll line = ff.value(); line.graph->addData(key,vv); //更新标签位置和内容 if(line.tag) { double graphValue = line.graph->dataMainValue(line.graph->dataCount()-1); line.tag->updateTracerPosition(key,graphValue); line.tag->setText(QString::number(graphValue, 'f', 2)); } } } // make key axis range scroll with the data m_plot->xAxis->setRange(key, m_nXlength, Qt::AlignRight); //50是X轴的长度 m_plot->replot(); } // 设置游标标签的显示和隐藏 void PlotBaseWidget::ShowTagLabels(bool b) { QMap::Iterator it = m_mapLineInfo.begin(); //曲线属性 for(; it != m_mapLineInfo.end(); it++) { StLineInfoAll st = it.value(); if(st.tag) st.tag->setVisible(b); } m_plot->replot(); } // 设置数值游标的显示和隐藏 /* void PlotBaseWidget::ShowValueTracer(bool b) { QMap::Iterator it = m_mapLineInfo.begin(); //曲线属性 for(; it != m_mapLineInfo.end(); it++) { StLineInfoAll st = it.value(); if(st.vtrac) st.vtrac->setVisible(b); } m_plot->replot(); }*/ // 设置X轴范围 void PlotBaseWidget::SetXrange(int id,double lower, double upper) { if(id == 0) m_plot->xAxis->setRange(lower,upper); //设置X轴范围 else m_plot->xAxis2->setRange(lower,upper); } // 设置Y轴范围 void PlotBaseWidget::SetYrange(int id,double lower, double upper) { if(id == 0) m_plot->yAxis->setRange(lower,upper); else m_plot->yAxis2->setRange(lower,upper); } // 设置X轴窗口长度(动态曲线使用) void PlotBaseWidget::SetXLength(int w) { m_nXlength = w; } //显示数值游标 void PlotBaseWidget::slotShowValueTracer(QMouseEvent *event) { double x = m_plot->xAxis->pixelToCoord(event->pos().x()); QMap::Iterator it = m_mapLineInfo.begin(); for(; it != m_mapLineInfo.end();it++) { StLineInfoAll info = it.value(); double y = 0; QSharedPointer tmpContainer; tmpContainer = info.graph->data(); //使用二分法快速查找所在点数据!!!敲黑板,下边这段是重点 int low = 0, high = tmpContainer->size(); while(high > low) { int middle = (low + high) / 2; if(x constBegin()->mainKey() || x > (tmpContainer->constEnd()-1)->mainKey()) break; if(x == (tmpContainer->constBegin() + middle)->mainKey()) { y = (tmpContainer->constBegin() + middle)->mainValue(); break; } if(x > (tmpContainer->constBegin() + middle)->mainKey()) { low = middle; } else if(x constBegin() + middle)->mainKey()) { high = middle; } if(high - low constBegin()+low)->mainValue() + ( (x - (tmpContainer->constBegin() + low)->mainKey()) * ((tmpContainer->constBegin()+high)->mainValue() - (tmpContainer->constBegin()+low)->mainValue()) ) / ((tmpContainer->constBegin()+high)->mainKey() - (tmpContainer->constBegin()+low)->mainKey()); break; } } if(info.vtrac) { info.vtrac->updateTracerPosition(x, y); info.vtrac->setText(QString::number(y, 'f', 2)); } } m_plot->replot(); } // 初始化参数 void PlotBaseWidget::InitParam() { m_nXlength = 50; m_nYchanged = -1; //初始化默认颜色列表 { m_listColorDef.push_back(QColor(250, 120, 0)); m_listColorDef.push_back(QColor(0, 180, 60)); m_listColorDef.push_back(Qt::green); m_listColorDef.push_back(Qt::yellow); m_listColorDef.push_back(Qt::black); m_listColorDef.push_back(Qt::blue); m_listColorDef.push_back(Qt::red); m_listColorDef.push_back(Qt::darkCyan); } } // 获取可用颜色 QColor PlotBaseWidget::GetUsefullColor(int i) { if(i>= 0 && i setupUi(this); QDesktopWidget* pDesktopWidget = QApplication::desktop(); //获取可用桌面大小 QRect deskRect = pDesktopWidget->availableGeometry(); //获取主屏幕分辨率 //QRect screenRect = pDesktopWidget->screenGeometry(); setMinimumSize(deskRect.width(),deskRect.height()); m_dock = new PlotBaseWidget(this,deskRect.width()/2,deskRect.height()/2); m_dock->move(0,10); QMap mm; StLineInfo st1; st1.name = "test"+QString::number(1); mm.insert(PARAM_MYTEST_1,st1); st1.name = "test"+QString::number(2); mm.insert(PARAM_MYTEST_2,st1); m_dock->CreateGraph(mm); m_dock->SetXrange(0,0,1000); m_dock->SetXLength(40); m_dock->SetYrange(1,-5,5); //m_dock->ShowTagLabels(false); m_timer = new QTimer(this); connect(m_timer,SIGNAL(timeout()),this,SLOT(slotTimeout())); } MainWindow::~MainWindow() { delete ui; } //开始-终止模拟 void MainWindow::on_btnStart_clicked() { if(m_timer->isActive()) { m_timer->stop(); m_dock->ShowTagLabels(false); ui->btnStart->setText(QString::fromUtf8("开始")); }else { m_timer->start(50); m_dock->ShowTagLabels(true); ui->btnStart->setText(QString::fromUtf8("结束")); } } // 模拟数据 void MainWindow::slotTimeout() { static QTime time(QTime::currentTime()); // calculate two new data points: double key = time.elapsed()/1000.0; // time elapsed since start of demo, in seconds static double lastPointKey = 0; if (key-lastPointKey > 0.002) // at most add point every 2 ms { // add data to lines double vv1 = qSin(key)+qrand()/(double)RAND_MAX*1*qSin(key/0.3843); double vv2 = qCos(key)+qrand()/(double)RAND_MAX*0.5*qSin(key/0.4364); QMap mapData; mapData.insert(PARAM_MYTEST_1,vv1); mapData.insert(PARAM_MYTEST_2,vv2); m_dock->AddData(key,mapData); lastPointKey = key; } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3