效果图如下:
具体功能如下:
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<QCustomPlot> _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<QCustomPlot> m_parentPlot; //曲线图
QCPGraph *m_graph; //这里是存传入的绘图图层
QPointer<QCPItemTracer> m_tracer; //数据跟踪点
QPointer<QCPItemLine> m_arrowLine; //线元素
QPointer<QCPItemText> m_labelText; //数据跟踪点文本元素
PlotValueTracerType m_type; //追踪点类型
int m_nLineLength; //线元素的长度
};
#endif // PLOTVALUETRACER_H
PlotValueTracer.cpp代码
#include "PlotValueTracer.h"
PlotValueTracer::PlotValueTracer(QPointer<QCustomPlot> _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<int,StLineInfo> mapLineInfo);
/**
* @brief 添加数据
* @param key 键值
* @param mapData 数据内容,键为数据类型标识,值为key对应的值
*/
void AddData(double key,QMap<int,double> 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<QCPGraph> graph; //图层标识
QPointer<PlotValueTracer> tag; //游标
QPointer<PlotValueTracer> vtrac; //数值游标
StLineInfo info; //曲线信息
StLineInfoAll(){graph = 0; tag = 0;vtrac = 0;}
};
QMap<int,StLineInfoAll> m_mapLineInfo; //曲线属性
QList<QColor> 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<int,StLineInfo> 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<int,StLineInfo>::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<int,double> mapData)
{
QMap<int, double>::Iterator it = mapData.begin();
for(; it != mapData.end(); it++)
{
double vv = it.value();
QMap<int,StLineInfoAll>::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<int,StLineInfoAll>::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<int,StLineInfoAll>::Iterator it = m_mapLineInfo.begin();
for(; it != m_mapLineInfo.end();it++)
{
StLineInfoAll info = it.value();
double y = 0;
QSharedPointer<QCPGraphDataContainer> tmpContainer;
tmpContainer = info.graph->data();
//使用二分法快速查找所在点数据!!!敲黑板,下边这段是重点
int low = 0, high = tmpContainer->size();
while(high > low)
{
int middle = (low + high) / 2;
if(x < tmpContainer->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 < (tmpContainer->constBegin() + middle)->mainKey())
{
high = middle;
}
if(high - low <= 1)
{ //差值计算所在位置数据
y = (tmpContainer->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 < m_listColorDef.size())
return m_listColorDef.at(i);
else
{
//QColor c(222,222,222);
QColor c(qSin(i*0.3)*100+100, qSin(i*0.6+0.7)*100+100, qSin(i*0.4+0.6)*100+100);
m_listColorDef.push_back(c);
return c;
}
}
MainWindow.h中代码
private slots:
void on_btnStart_clicked(); //按钮按下
void slotTimeout(); //定时器槽函数,用于模拟数据
private:
Ui::MainWindow *ui;
QTimer* m_timer; //定时器
PlotBaseWidget* m_dock; //曲线窗口
MainWindow.cpp代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->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<int,StLineInfo> 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<int,double> mapData;
mapData.insert(PARAM_MYTEST_1,vv1);
mapData.insert(PARAM_MYTEST_2,vv2);
m_dock->AddData(key,mapData);
lastPointKey = key;
}
}