ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

由QCustomplot引发drawPolyline和drawLine的区别

2022-01-16 16:58:12  阅读:234  来源: 互联网

标签:... Qt drawLine 50 QCustomplot drawPolyline 100 include painter


标题

一、起因

最近在使用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));//注意这里线宽大于1
		plot_->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*34712.4%非常卡顿100 / (55 - 50) * 347 = 6940
QPen(Qt::red, 2)(0,100)2160x144012%卡顿100 / (100 - 0) * 1440 = 1440
QPen(Qt::red, 2)(0,100)754*3478%较为卡顿100 / (100 - 0) * 347 = 347
QPen(Qt::red, 1)(50,55)754*3471%流畅100 / (55 - 50) * 347 = 6940
QPen(Qt::red, 1)(0,100)754*3470.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. 下面来看drawLinePlot函数,此函数位于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*3470.6%流畅
QPen(Qt::red, 2)(50, 55)2160x14407%流畅

从源码注释也可看出

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_NAMESPACE

class 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();
}

测试结果如下:
在这里插入图片描述

标签:...,Qt,drawLine,50,QCustomplot,drawPolyline,100,include,painter
来源: https://blog.csdn.net/kpengk/article/details/122522532

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有