QPainter drawPolyline和drawLine

  • 一、起因
  • 二、原因分析
    • 2.1 测试
    • 2.1 查看QCustomplot中绘制源码
  • 三、解决方法
  • 四、drawPolyline和drawLine的区别
  • 五、对QCharts进行测试

一、起因

最近在使用QCustomplot绘制曲线图时,遇到性能低下(卡顿甚至无响应)。测试的功能为用rand()函数动态生成1000个数据点,其数据值范围为0<=X<1000, 0<=Y<100, X步进=0.1,其代码如下:

#include <QApplication>
#include <QHBoxLayout>
#include <QWidget>
#include <QTimer>
#include "qcustomplot.h"class WindowCustomPlot :public QWidget
{public:explicit WindowCustomPlot(QWidget* parent = nullptr):QWidget{ parent },plot_{ new QCustomPlot{ this } }{auto layout = new QHBoxLayout{ this };layout->addWidget(plot_);resize(500, 200);auto line = plot_->addGraph();line->setPen(QPen(Qt::red, 2));//注意这里线宽大于1plot_->xAxis->setRange(0, 500);plot_->yAxis->setRange(50, 55);//注意这里设置的Y轴范围,而产生的数据为0~100直接index_.reserve(500);value_.reserve(500);auto timer = new QTimer{ this };connect(timer, &QTimer::timeout, this, [this, timer, line] {if (value_.count() >= 500) {timer->stop();}index_.push_back(index_.size());value_.push_back(rand() % 100);line->setData(index_, value_);plot_->replot();});timer->start(35);}
private:QCustomPlot* plot_;QVector<qreal> index_;QVector<qreal> value_;
};int main(int argc, char* argv[])
{QApplication a(argc, argv);WindowCustomPlot w;w.show();return a.exec();
}

注意代码中的第19行和21行(line->setPen(QPen(Qt::red, 2));, plot_->yAxis->setRange(50, 55);)。设置了Y轴范围为50~55,即只能显示部分图像。其运行结果如下,当数据点数为100个左右开始出现明显卡顿,且CPU占用高。

二、原因分析

2.1 测试

测试环境如下:

  • 开发环境:Qt6.2、vs2022;
  • CPU:Core i5-1135G7、4核8线程;
画笔宽度 Y轴范围 窗口大小 CPU占用 测试现象 Y轴取值对应像素高度
QPen(Qt::red, 2) (50,55) 754*347 12.4% 非常卡顿 100 / (55 - 50) * 347 = 6940
QPen(Qt::red, 2) (0,100) 2160x1440 12% 卡顿 100 / (100 - 0) * 1440 = 1440
QPen(Qt::red, 2) (0,100) 754*347 8% 较为卡顿 100 / (100 - 0) * 347 = 347
QPen(Qt::red, 1) (50,55) 754*347 1% 流畅 100 / (55 - 50) * 347 = 6940
QPen(Qt::red, 1) (0,100) 754*347 0.6% 流畅 100 / (100 - 0) * 347 = 347

经过上述测试可以看出当画笔宽度大于1且点坐标映射到像素坐标后像素坐标取值范围较大时将出现卡顿现象,也可以理解为将图放大到一定程度。

2.1 查看QCustomplot中绘制源码

  1. 调用plot_->replot()后,在replot中将对各个图层进行绘制,如坐标轴、曲线、文字、图例等;
void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
{...//绘制各个图层foreach (QCPLayer *layer, mLayers)layer->drawToPaintBuffer();...
}void QCPLayer::drawToPaintBuffer()
{...if (painter->isActive())draw(painter);//利用多态调用相应绘制函数...
}void QCPGraph::draw(QCPPainter *painter)
{...getLines(&lines, lineDataRange);//转换为像素坐标...drawLinePlot(painter, lines); //绘制曲线...
}void QCPGraph::drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const
{...drawPolyline(painter, lines);...
}
  1. 下面来看drawPolyline函数,此函数位于qcustomplot.h中,其中根据绘制标志不同,选择drawLine或者drawPolyline函数。而上述测试代码则默认选择的drawPolyline函数。
template <class DataType>
void QCPAbstractPlottable1D<DataType>::drawPolyline(QCPPainter *painter, const QVector<QPointF> &lineData) const
{...if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) && painter->pen().style() == Qt::SolidLine &&!painter->modes().testFlag(QCPPainter::pmVectorized) && !painter->modes().testFlag(QCPPainter::pmNoCaching)){...while (i < lineDataSize) {...painter->drawLine(lineData.at(i-1), lineData.at(i));...}} else{...// draw last segment:painter->drawPolyline(lineData.constData()+segmentStart, lineDataSize-segmentStart);}
}

三、解决方法

长上述源码可以看到,使用QCP::phFastPolylines则可以使用drawLine函数绘制,性能将明显提升。设置快速绘制标志的方法为plot_->setPlottingHint(QCP::phFastPolylines);

设置为QCP::phFastPolylines后,进行性能测试:

画笔宽度 Y轴范围 窗口大小 CPU占用 测试现象
QPen(Qt::red, 2) (50, 55) 754*347 0.6% 流畅
QPen(Qt::red, 2) (50, 55) 2160x1440 7% 流畅

从源码注释也可看出

enum PlottingHint {phNone              = 0x000 /// No hints are set,phFastPolylines    = 0x001 /// Graph/Curve lines are drawn with a faster method. This reduces the quality especially of the line segment joins, thus is most effective for pen sizes larger than 1. It is only used for solid line pens.,phImmediateRefresh = 0x002 /// causes an immediate repaint() instead of a soft update() when QCustomPlot::replot() is called with parameter QCustomPlot::rpRefreshHint. This is set by default to prevent the plot from freezing on fast consecutive replots (e.g. user drags ranges with mouse).,phCacheLabels      = 0x004 /// axis (tick) labels will be cached as pixmaps, increasing replot performance.
};

phFastPolylines:用更快的方法绘制图形/曲线的线,但是会降低质量,特别是线段连接处的质量,因此对线宽大于1的画笔最有效。 它只用于实线。

四、drawPolyline和drawLine的区别

上面说会降低绘制质量,特别是线段连接处。下面看看drawPolyline和drawLine绘制出的线段的区别。

测试代码如下:

#include <QApplication>
#include <QHBoxLayout>
#include <QWidget>
#include <QPainter>class WindowLine :public QWidget
{public:explicit WindowLine(QWidget* parent = nullptr) :QWidget{ parent } {resize(500, 200);}protected:void paintEvent(QPaintEvent* event) override {QPainter painter(this);painter.setPen(QPen(Qt::red, 16));painter.setRenderHint(QPainter::Antialiasing);QVector<QPointF> data1{ { 50, 180 },{ 100, 50 },{ 150, 180 } };for (int i = 1; i < data1.count(); ++i) {painter.drawLine(data1.at(i - 1), data1.at(i));}QVector<QPointF> data2{ { 250, 180 },{ 300, 50 },{ 350, 180 } };painter.drawPolyline(data2.constBegin(), data2.count());}
};int main(int argc, char* argv[])
{QApplication a(argc, argv);WindowLine w;w.show();return a.exec();
}

从上图可以看出drawPloyline绘制的线段会对连接处进行裁剪等操作,使连接处更加圆滑,这也将增加更多的计算,由此导致性能低下。对上图中的绘制进行耗时测量,测量结果如下(有3~5倍的差距):

五、对QCharts进行测试

对QCharts的测试显示,默认使用drawLine方式进行绘制。
测试结果如下:

测试代码如下:

#include <QApplication>
#include <QHBoxLayout>
#include <QWidget>
#include <QTimer>
#include <QtCharts/QChartView>
#include <QtCharts/QLineSeries>QT_USE_NAMESPACEclass ChartWindow :public QWidget
{public:explicit ChartWindow(QWidget* parent = nullptr) :QWidget{ parent } {resize(500, 500);QVector<QPointF> data{ { 200, 0}, { 250, 180 },{ 300, 50 },{ 350, 200 } };QLineSeries* series = new QLineSeries();series->setPen(QPen(Qt::red, 18));series->replace(data);QChart* chart = new QChart();chart->legend()->hide();chart->addSeries(series);chart->createDefaultAxes();QChartView* chartView = new QChartView(chart);chartView->setRenderHint(QPainter::Antialiasing);auto layout = new QHBoxLayout{ this };layout->addWidget(chartView);}
};int main(int argc, char* argv[])
{QApplication a(argc, argv);ChartWindow w;w.show();return a.exec();
}

由QCustomplot引发drawPolyline和drawLine的区别相关推荐

  1. C#规范整理·异常与自定义异常

    这里会列举在C#中处理CLR异常方面的规范,帮助大家构建和开发一个运行良好和可靠的应用系统. 前言   迄今为止,CLR异常机制让人关注最多的一点就是"效率"问题.其实,这里存在认 ...

  2. QCostomPlot 示例注解 3

    Item 在QCustomPlot中,所有的Item都继承自QCPAbstractItem,QCustomPlot给我们提供了一些Item item 说明 QCPItemBracket 括号Item ...

  3. 【GCC】gcc警告选项汇总--编辑中|gcc编译选项

    目录 前言 请求或取消警告选项 GCC编译选项 参考原文:https://blog.csdn.net/qq_17308321/article/details/79979514 前言 警告:不是错误的, ...

  4. gcc警告选项汇总 转

    ---------------- 原文链接:https://blog.csdn.net/qq_17308321/article/details/79979514 参考资料:https://gcc.gn ...

  5. 架构、框架和设计模式

    最近要填写项目经验,想把去年写的一个网站(Spring MVC)经验写上去装一下,刚百度了一个Spring MVC就搜出了一堆东西,然后想当然的搜了一个三层架构(这是学软件工程的时候提过的),以为就是 ...

  6. 恋与机器人:“她”和“杰克茜”,虚拟助理的无穷魅力

    全文共2371字,预计学习时长6分钟 <她>中的Joaquin Phoenix 人类的感情美好而奇妙:这些年,通常是在第四频道离奇的纪录片,我常常听说一些人们爱上了各种各样事物的故事. 有 ...

  7. 5.22 locahost仅是本地服务域名。★★★v-show引发refs不渲染, created和mounted,npm install xxx区别,display,visibility,opac区

    1.新手会误将localhost域名作为开发域名一用到底. 今天做移动端本地前后联调时,误以为http://localhost:8080就是本地域名,这种说法是正确的,但是这是本地的修改后的域名,真实 ...

  8. 由一道考研基础题引发的关于对(函数导数符号在内外的区别)f‘(x)和[f(x)]‘的区别思考

    笔者今天在上课的时候,由于一道题和老师解法大相径庭而陷入思考 题目如下: 这是道02年的数学考研题目填空题第二道,事后想想确实是道非常基础的题目. 下面先奉上老师的标准解法: 很经典的换元思想. 当时 ...

  9. 由小米10拆机引发联想——硬件工程师与维修工程师的区别

    近期看了很多手机维修师傅上传在各种平台的视频,感觉即治愈又精彩,最为一个硬件工程师开始思考这两者的区别.说实话一开始我是觉着手机维修老师单纯为了精彩而精彩,为了炫技而炫技,并且是野路子出身.直到昨天我 ...

  10. Python中printf函数中逗号引发的区别

    (1)异常的输出没有保留3位小数位共5位有效数字 (2)正常的输出

最新文章

  1. OpenCV——KNN分类算法 摘
  2. 常用插值算法介绍(二)
  3. feign post 传递空值_HTTP中GET与POST的区别,99 %的人都理解错了
  4. 如何快速提高网站流量
  5. kafka中controller的作用_Kafka 常见问题汇总
  6. VTK:相机模糊CameraBlur用法实战
  7. 手算KMP匹配的Next值和Nextval值(转载)
  8. javascript array添加图片_史上最全的web前端面试题汇总及答案JavaScript之二(二)...
  9. ios 横向滚轮效果_ios横向菜单+页面滑动
  10. mysql boolean_产品操作MySQL第7篇 – 运算符 – IS NULL
  11. ai面试的优缺点_面试也能开“外挂”?领英AI做了什么
  12. msf出现Database not connected等问题【已解决】
  13. HTTP权威指南------1.1HTTP概述
  14. EOS Bios Boot Sequence 启动步骤
  15. 我需要HCNE模拟考试系统
  16. 微信公众号后台添加安全域名 提示:无法访问xxx指向的web服务器(或虚拟主机)的目录,请检查网络设置
  17. 【人工智能II】实验2 强化学习Q-Learning算法
  18. 蓝牙BQB认证的过程与方式(SIG)
  19. 100多个新媒体人实用网站
  20. android复制sim卡上的短信到手机,将数据从SIM卡轻松传输到新手机的最佳方法

热门文章

  1. UCF101数据集提取帧+TDN部署(Anaconda+Python3.7+Pytorch)
  2. 使用Pytorch实现UCF101视频分类
  3. 微软商店下载的软件怎么放到桌面?
  4. windows10 1909 X64位 精简优化珍藏版
  5. CSS行内样式内部样式外部样式
  6. 怎么样可以通过阿里云APP进行备案 阿里云备案一般多长时间
  7. 基于Java+SpringBoot+vue+elementui药品商城采购系统详细设计实现
  8. elasticsearch之 hdfs上的备份和还原操作
  9. 杰奇python采集器_linux下安装杰奇,实现关关采集器远程采集详细教程
  10. web前端之JavaScript高级程序设计六:事件