提示:这里是该系列文章的所有文章的目录
第一章: (一)QCustomPlot常见属性设置、多曲线绘制、动态曲线绘制、生成游标、矩形放大等功能实现
第二章: (二)QCustomPlot生成热力图/矩阵颜色图
第三章: (三)Qt+QCustomPlot生成上下方向/不同颜色的条形图(柱形图)
第四章 :(四)QCustomPlot柱形图动态显示实例开发
在最近的项目开发过程中,需要进行大量数据(千万级)的绘制显示,发现Qt+QCustomPlot能够完美解决该需求,在此对这段时间的学习进行下记录总结,下面将新建一个Test项目来进行QCustomPlot的学习。
Test项目主要功能
1.使用定时器进行数据添加模拟
2.界面上同时绘制三条曲线
3.界面动态显示添加的数据
4.生成游标可以跟随鼠标显示相应点位信息
5.鼠标右键实现矩形框选放大功能
项目效果
提示:以下是本篇文章正文内容,下面案例可供参考,完整项目代码展示在第五节。
QCustomPlot是一个用于绘图和数据可视化的Qt c++小部件。它没有进一步的依赖性,并且有很好的文档。这个绘图库专注于制作好看、出版质量高的2D绘图、图形和图表,以及为实时可视化应用程序提供高性能。
QCustomPlot官网链接:https://www.qcustomplot.com/index.php/introduction
下载链接:https://www.qcustomplot.com/index.php/download
我下载好的百度网盘链接:https://pan.baidu.com/s/1sx1NvzlSg_18WKhtsJqdjA
提取码:xxcj
下载压缩包后直接解压就行,可以看到以下文件:
2.在该路径下新建一个CustomPlot文件夹,将之前解压获取的qcustomplot.h文件和qcustomplot.cpp文件复制到里面,然后新建一个txt文本文件,里面输入以下内容,完成后将.txt后缀修改为.pri
HEADERS += \
$$PWD/qcustomplot.h
SOURCES += \
$$PWD/qcustomplot.cpp
3.回到QtCreator项目页,点击Test.pro,进行以下修改,完成第三步后可以看到CustomPlot文件夹会自动显示在项目树上
1.进入Qt设计师界面,选择一个Widget控件拖动至界面上选中,点击右键–>提升为…,在弹出的窗口上进行输入
话不多说,这边直接贴入代码,相关属性设置及功能见代码注释
1.mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include "CustomPlot/qcustomplot.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void InitWidget();
void InitCustomPlot();
private slots:
void slot_DataTimeOut();
void slot_CusTimeOut();
void slot_SelectionChanged();
void mousePress(QMouseEvent* mevent);
void mouseMove(QMouseEvent *mevent);
void mouseRelease(QMouseEvent *mevent);
void on_action_start_triggered();
void on_action_stop_triggered();
void on_action_recovery_triggered();
private:
Ui::MainWindow *ui;
QCustomPlot *m_Multichannel;
QCPItemTracer* tracer;
QCPItemText* tracerLabel;
QCPGraph *tracerGraph;
QRubberBand *rubberBand;
QPoint rubberOrigin;
QTimer *dataTimer;
QTimer *cusTimer;
int xCount;
bool chooseFlag;
};
#endif // MAINWINDOW_H
2.mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("QCustomPlot");
InitWidget();
InitCustomPlot();
}
MainWindow::~MainWindow()
{
delete m_Multichannel;
delete ui;
}
void MainWindow::InitWidget()
{
//初始化一些变量
xCount = 0;
chooseFlag = false;
ui->action_start->setEnabled(true);
ui->action_stop->setEnabled(false);
//数据添加定时器
dataTimer = new QTimer();
connect(dataTimer,SIGNAL(timeout()),this,SLOT(slot_DataTimeOut()));
//界面更新定时器
cusTimer = new QTimer();
connect(cusTimer,SIGNAL(timeout()),this,SLOT(slot_CusTimeOut()));
}
void MainWindow::InitCustomPlot()
{
m_Multichannel = new QCustomPlot();
m_Multichannel = ui->widget;
//m_Multichannel->axisRect()->setBackground(QBrush(Qt::black)); //设置背景颜色
for(int i=0;i<3;i++)
{
m_Multichannel->addGraph(); //添加数据曲线
//m_Multichannel->graph(i)->setPen(QPen(Qt::black)); //设置相同颜色的曲线
}
//QPen pen;
//pen.setWidth(1); //设置选中时的线宽 建议宽度设为1,如果数据量很大,界面会卡顿
//pen.setColor(Qt::blue);
//m_Multichannel->graph(0)->setPen(pen); //这种方式不如下面的方便,屏蔽
m_Multichannel->graph(0)->setPen(QPen(Qt::black)); //设置曲线颜色
m_Multichannel->graph(0)->setName("曲线一"); //设置曲线名称
m_Multichannel->graph(1)->setPen(QPen(Qt::red));
m_Multichannel->graph(1)->setName("曲线二");
m_Multichannel->graph(2)->setPen(QPen(Qt::green));
m_Multichannel->graph(2)->setName("曲线三");
//m_Multichannel->graph(0)->setBrush(QBrush(QColor(0,0,255,20))); //在点击对应曲线时 区域变色
//m_Multichannel->graph(0)->setAntialiasedFill(true); //设置填充
//m_Multichannel->graph(0)->setVisible(false); //设置可见性
//x轴设置
QSharedPointer<QCPAxisTickerFixed> intTicker_M(new QCPAxisTickerFixed);
intTicker_M->setTickStep(1); //设置刻度之间的步长为1
intTicker_M->setScaleStrategy(QCPAxisTickerFixed::ssMultiples); //设置缩放策略
m_Multichannel->xAxis->setTicker(intTicker_M); //应用自定义整形ticker,防止使用放大功能时出现相同的x刻度值
m_Multichannel->xAxis->ticker()->setTickCount(11); //刻度数量
m_Multichannel->xAxis->setNumberFormat("f"); //x轴刻度值格式
m_Multichannel->xAxis->setNumberPrecision(0); //刻度值精度
m_Multichannel->xAxis->setLabel("数量(n)"); //设置标签
m_Multichannel->xAxis->setLabelFont(QFont(font().family(),8)); //设置标签字体大小
m_Multichannel->xAxis->setRange(0,10,Qt::AlignLeft); //范围
m_Multichannel->xAxis->setSubTickLength(0,0); //子刻度长度
m_Multichannel->xAxis->setTickLength(10,5); //主刻度长度
//y轴设置
m_Multichannel->yAxis->setNumberFormat("f");
m_Multichannel->yAxis->setNumberPrecision(2);
m_Multichannel->yAxis->setLabel("距离(m)");
m_Multichannel->yAxis->setLabelFont(QFont(font().family(),8));
m_Multichannel->yAxis->setRange(0,5);
m_Multichannel->yAxis->setTickLength(10,5);
m_Multichannel->setInteractions(QCP::iNone); //设置不与鼠标交互
//m_Multichannel->setInteractions(QCP::iSelectPlottables | QCP::iSelectLegend | QCP::iRangeDrag); //设置鼠标交互,曲线及图例可点击,可拖动
m_Multichannel->legend->setVisible(true); //设置图例可见
m_Multichannel->legend->setBrush(QColor(255,255,255,0)); //设置背景透明
m_Multichannel->axisRect()->insetLayout()->setInsetAlignment(0,Qt::AlignTop|Qt::AlignRight); //设置图例居右上
//游标
tracer = new QCPItemTracer(m_Multichannel); //生成游标
m_Multichannel->setMouseTracking(true); //让游标自动随鼠标移动,若不想游标随鼠标动,则禁止
//tracer->setPen(QPen(QBrush(QColor(Qt::red)),Qt::DashLine)); //虚线游标
tracer->setPen(QPen(Qt::red)); //圆圈轮廓颜色
tracer->setBrush(QBrush(Qt::red)); //圆圈圈内颜色
tracer->setStyle(QCPItemTracer::tsCircle); //圆圈
tracer->setSize(5);
//tracer->setVisible(false); //设置可见性
//游标说明
tracerLabel = new QCPItemText(m_Multichannel); //生成游标说明
//tracerLabel->setVisible(false); //设置可见性
tracerLabel->setLayer("overlay"); //设置图层为overlay,因为需要频繁刷新
tracerLabel->setPen(QPen(Qt::black)); //设置游标说明颜色
tracerLabel->setPositionAlignment(Qt::AlignLeft | Qt::AlignTop); //左上
tracerLabel->setFont(QFont(font().family(),10)); //字体大小
tracerLabel->setPadding(QMargins(4,4,4,4)); //文字距离边框几个像素
tracerLabel->position->setParentAnchor(tracer->position); //设置标签自动随着游标移动
//选择不同的曲线
connect(m_Multichannel,SIGNAL(selectionChangedByUser()),this,SLOT(slot_SelectionChanged()));
//初始化QRubberBand //矩形放大
rubberBand = new QRubberBand(QRubberBand::Rectangle,m_Multichannel);
//连接鼠标事件发出的信号,实现绑定
connect(m_Multichannel,SIGNAL(mousePress(QMouseEvent*)),this,SLOT(mousePress(QMouseEvent*)));
connect(m_Multichannel,SIGNAL(mouseMove(QMouseEvent*)),this,SLOT(mouseMove(QMouseEvent*)));
connect(m_Multichannel,SIGNAL(mouseRelease(QMouseEvent*)),this,SLOT(mouseRelease(QMouseEvent*)));
//lambda表达式 mouseMoveEvent
connect(m_Multichannel,&QCustomPlot::mouseMove,[=](QMouseEvent* event){
if(tracer->graph() == nullptr)
{
return;
}
if(tracer->graph()->data()->isEmpty())
{
return;
}
if(tracer->visible())
{
if(tracerGraph)
{
double x = m_Multichannel->xAxis->pixelToCoord(event->pos().x());
tracer->setGraphKey(x); //将游标横坐标设置成刚获得的横坐标数据x
//tracer->setInterpolating(true); //自动计算y值,若只想看已有点,不需要这个
tracer->updatePosition(); //使得刚设置游标的横纵坐标位置生效
tracerLabel->setText(QString("x:%1\ny:%2").arg(tracer->position->key()).arg(tracer->position->value()));
m_Multichannel->replot(QCustomPlot::rpQueuedReplot);
}
}
});
}
//多通道界面曲线改变
void MainWindow::slot_SelectionChanged()
{
for(int i=0;i<3;i++)
{
QCPGraph *graph = m_Multichannel->graph(i);
if(graph == nullptr)
{
return;
}
QCPPlottableLegendItem *item = m_Multichannel->legend->itemWithPlottable(graph);
if(item->selected() || graph->selected()) //选中了哪条曲线或者曲线的图例
{
tracerGraph = graph;
if(tracer != nullptr)
{
tracer->setGraph(tracerGraph);
}
item->setSelected(true);
QPen pen;
pen.setWidth(1); //设置选中时的线宽 建议宽度设为1,如果数据量很大,界面会卡顿
pen.setColor(Qt::blue);
graph->selectionDecorator()->setPen(pen);
graph->setSelection(QCPDataSelection(graph->data()->dataRange()));
}
}
}
//添加数据定时器
void MainWindow::slot_DataTimeOut()
{
m_Multichannel->graph(0)->addData(xCount,1); //添加数据使用addData
m_Multichannel->graph(1)->addData(xCount,3);
m_Multichannel->graph(2)->addData(xCount,5);
xCount++;
}
//界面更新定时器
void MainWindow::slot_CusTimeOut()
{
m_Multichannel->xAxis->setRange(0,xCount>10?xCount:10,Qt::AlignLeft); //xCount代表你的数据量
m_Multichannel->yAxis->setRange(0,6); //如果需要y轴自适应数据 就需要获取数据的最小最大值依次填入
m_Multichannel->replot(QCustomPlot::rpQueuedReplot); //括号内参数作用是避免重复绘制
}
//鼠标按下槽函数
void MainWindow::mousePress(QMouseEvent* mevent)
{
if(chooseFlag)
{
if(mevent->button() == Qt::RightButton) //鼠标右键实现放大功能
{
rubberOrigin = mevent->pos();
rubberBand->setGeometry(QRect(rubberOrigin, QSize()));
rubberBand->show();
}
}
}
//鼠标移动槽函数
void MainWindow::mouseMove(QMouseEvent *mevent)
{
if(chooseFlag)
{
if(rubberBand->isVisible())
{
rubberBand->setGeometry(QRect(rubberOrigin, mevent->pos()).normalized());
}
}
}
//鼠标释放槽函数
void MainWindow::mouseRelease(QMouseEvent *mevent)
{
if(chooseFlag)
{
Q_UNUSED(mevent);
if(rubberBand->isVisible())
{
const QRect zoomRect = rubberBand->geometry();
int xp1, yp1, xp2, yp2;
zoomRect.getCoords(&xp1, &yp1, &xp2, &yp2);
double x1 = m_Multichannel->xAxis->pixelToCoord(xp1);
double x2 = m_Multichannel->xAxis->pixelToCoord(xp2);
double y1 = m_Multichannel->yAxis->pixelToCoord(yp1);
double y2 = m_Multichannel->yAxis->pixelToCoord(yp2);
m_Multichannel->xAxis->setRange(x1, x2);
m_Multichannel->yAxis->setRange(y1, y2);
rubberBand->hide();
m_Multichannel->replot(QCustomPlot::rpQueuedReplot);
}
}
}
void MainWindow::on_action_start_triggered()
{
//初始化
xCount = 0;
for(int i=0;i<3;i++)
{
m_Multichannel->graph(i)->data().data()->clear(); //清除当前曲线数据
}
m_Multichannel->xAxis->setRange(0,10,Qt::AlignLeft);
m_Multichannel->yAxis->setRange(0,5);
m_Multichannel->replot(QCustomPlot::rpQueuedReplot);
chooseFlag = false;
dataTimer->start(100); //设置取数频率
cusTimer->start(1000); //设置更新界面频率
m_Multichannel->setInteractions(QCP::iSelectPlottables | QCP::iSelectLegend); //设置鼠标交互,曲线及图例可点击
ui->action_start->setEnabled(false);
ui->action_stop->setEnabled(true);
}
void MainWindow::on_action_stop_triggered()
{
chooseFlag = true; //该标志代表界面更新定时器停止后,可以对界面进行放大操作
dataTimer->stop();
cusTimer->stop();
m_Multichannel->setInteractions(QCP::iSelectPlottables | QCP::iSelectLegend | QCP::iRangeDrag); //设置鼠标交互,曲线及图例可点击,可拖动
ui->action_start->setEnabled(true);
ui->action_stop->setEnabled(false);
}
void MainWindow::on_action_recovery_triggered()
{
if(chooseFlag)
{
m_Multichannel->xAxis->setRange(0,xCount>10?xCount:10,Qt::AlignLeft);
m_Multichannel->yAxis->setRange(0,6);
m_Multichannel->replot(QCustomPlot::rpQueuedReplot);
}
}
本文标题上的功能,在项目中均有体现。这里也是简单介绍了QCustomPlot的一些属性设置使用,而QCustomPlot还提供了大量能使我们快速便捷地设置属性的函数和方法,这些就需要我们持续的挖掘学习,加油!
下一篇主要讲述了QCustomPlot中的QCPColorMap热力图相关知识
本系列文章下一篇:(二)QCustomPlot生成热力图/矩阵颜色图
hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。
参考博客:QCustomPlot基础教程(一)——QCustomPlot的安装及基础实例