解决的问题
在绘制比较复杂的控件时,需要在paintEvent中书写大量的绘图代码。如果代码量巨大,且控件经常刷新,应用程序就会频繁的绘制这一坨大量的绘图代码,非常的浪费计算机资源。例如仪表盘,仪表盘需要绘制背景,边框,刻度,刻度数值,指针等,如果是涉及到渐变色,不规则图形过多的话,代码量就会很大,动辄一二百行去实现。
绘图优化的原理:将控件绘图部分根据频繁刷新度分离代码,例如仪表盘的背景和刻度,以及刻度数值是不需要频繁更换的,这些内容相较固定。顶多是控件大小发生变化,刻度区间发生变化等等情况时才需要重新绘制。相对来说,仪表盘的数值指针是需要频繁绘制的。
因此,将不频繁变动的部分在需要时绘制为QPixmap以png格式保存起来,在paintEvent中绘制该部分时,只需要绘制该QPixmap即可,这样可以优化绘图性能。
示例前奏
以绘制上图作为演示。
示例
#ifndef DASHBOARD_H
#define DASHBOARD_H
#include <QWidget>
#include <QPainter>
#include <QPainterPath>
#include <QBitmap>
#define PI 3.14159265
class DashBoard:public QWidget{
Q_OBJECT
public:
explicit DashBoard(QWidget* parent=nullptr);
~DashBoard();
inline void setVal(int val){_curVal=val;}
void setValRange(int minVal,int maxVal);
void getValRange(int& minVal,int& maxVal);
void setStartAngle(int angle);
protected:
void resizeEvent(QResizeEvent* event)override;
void paintEvent(QPaintEvent* e)override;
private:
void paintBackground();
void drawPointer(QPainter& painter);
private:
QPixmap _pixmap;
int _minVal=0;
int _maxVal=90;
int _curVal=50;
int _side;//直径
int _radius;//半径
int _startAngle=30;
int _x,_y;//绘图的起点
};
#endif // DASHBOARD_H
#include "dashboard.h"
DashBoard::DashBoard(QWidget* parent):QWidget(parent){}
DashBoard::~DashBoard(){}
void DashBoard::setValRange(int minVal,int maxVal){
_minVal=minVal;
_maxVal=maxVal;
paintBackground();
}
void DashBoard::getValRange(int &minVal,int &maxVal){
_minVal=minVal;
_maxVal=maxVal;
}
void DashBoard::setStartAngle(int angle){
if(angle>=180)return;
_startAngle=angle;
paintBackground();
}
void DashBoard::resizeEvent(QResizeEvent* e){
Q_UNUSED(e);
_pixmap=QPixmap(width(),height());
paintBackground();
}
void DashBoard::paintEvent(QPaintEvent* e){
Q_UNUSED(e);
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing|QPainter::TextAntialiasing,true);
painter.drawPixmap(0,0,_pixmap);
drawPointer(painter);
}
void DashBoard::paintBackground(){
_pixmap.fill();//清空QPixmap
int w=width();
int h=height();
_side=qMin(w,h);
_radius=_side/2.0;
_x=(w<h)?0:(w-h)/2;
_y=(h<w)?0:(h-w)/2;
//*******************************************************************************************************绘制背景
QPainter painter;
painter.begin(&_pixmap);
painter.setRenderHints(QPainter::Antialiasing|QPainter::TextAntialiasing,true);
QPainterPath path1,path2,path3,path4,path5,path6,path7,path8;
path1.addEllipse(QRectF(-_radius,-_radius,_side,_side));
path2.addEllipse(QRectF(-_radius*0.9,-_radius*0.9,_side*0.9,_side*0.9));
path3=path1-path2;
path4.addEllipse(QRectF(-_radius*0.85,-_radius*0.85,_side*0.85,_side*0.85));
path5=path2-path4;
path6.addEllipse(QRectF(-_radius*0.12,-_radius*0.12,_side*0.12,_side*0.12));
path7.addEllipse(QRectF(-_radius*0.1,-_radius*0.1,_side*0.1,_side*0.1));
path8=path6-path7;
QConicalGradient gradient;
gradient.setCenter(0,0);
gradient.setAngle(180);
gradient.setColorAt(0,0x828282);
gradient.setColorAt(0.125,0xd6d8d7);
gradient.setColorAt(0.25,0x484745);
gradient.setColorAt(0.375,0x060608);
gradient.setColorAt(0.5,0x494846);
gradient.setColorAt(0.625,0xd9d7d8);
gradient.setColorAt(0.75,0xb3b5b4);
gradient.setColorAt(0.875,0xf5f7f6);
gradient.setColorAt(1,0x828282);
QConicalGradient gradient1;
gradient1.setCenter(0,0);
gradient1.setAngle(180);
gradient1.setColorAt(0,0x110809);
gradient1.setColorAt(0.125,0x140c0a);
gradient1.setColorAt(0.25,0x49403b);
gradient1.setColorAt(0.375,0x534f4c);
gradient1.setColorAt(0.5,0x4a423f);
gradient1.setColorAt(0.625,0x2e2422);
gradient1.setColorAt(0.75,0xb3b5b4);
gradient1.setColorAt(0.875,0x000001);
gradient1.setColorAt(1,0x100a0a);
painter.save();
painter.translate(_x+_radius,_y+_radius);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(0x000001));
painter.drawPath(path1);
painter.setBrush(gradient1);
painter.drawPath(path5);
painter.setBrush(gradient);
painter.drawPath(path3);
painter.drawPath(path8);
painter.restore();
//*******************************************************************************************************绘制刻度
double unitAngle=(double)(360-_startAngle*2)/(double)(_maxVal-_minVal);
QLine line(QPoint(0,_radius*0.7),QPoint(0,_radius*0.8));
QLine line1(QPoint(0,_radius*0.74),QPoint(0,_radius*0.8));
QLine line2(QPoint(0,_radius*0.76),QPoint(0,_radius*0.8));
painter.save();
painter.translate(_x+_radius,_y+_radius);
painter.rotate(_startAngle-unitAngle);
for(int i=0;i<=(_maxVal-_minVal);i++){
painter.rotate(unitAngle);
if(i%10==0){
painter.setPen(QPen(QColor(0xFFFFFF),3.5));
painter.drawLine(line);
}else if(i%5==0){
painter.setPen(QPen(QColor(0xFFFFFF),2));
painter.drawLine(line1);
}else{
painter.setPen(QPen(QColor(0xFFFFFF),1));
painter.drawLine(line2);
}
}
painter.restore();
//*******************************************************************************************************绘制刻度数值
double len=_radius*0.63;
double len1=_radius*0.68;
QFont font("微软雅黑");
font.setPointSize(14);
QFont font1(font);
font1.setPointSize(10);
painter.save();
painter.translate(_x+_radius,_y+_radius);
painter.setPen(QPen(QColor(0xFFFFFF)));
for(int i=0;i<=(_maxVal-_minVal);i++){
if(i%10==0){
double nowAngle=(_startAngle+i*unitAngle)*PI/180.0;
QRectF dialRect(-len*qSin(nowAngle)-15,len*qCos(nowAngle)-15,30,30);
painter.setFont(font);
painter.drawText(dialRect,Qt::AlignCenter,QString::number(i+_minVal));
}else if(i%5==0){
double nowAngle=(_startAngle+i*unitAngle)*PI/180.0;
QRectF dialRect(-len1*qSin(nowAngle)-15,len1*qCos(nowAngle)-15,30,30);
painter.setFont(font1);
painter.drawText(dialRect,Qt::AlignCenter,QString::number(i+_minVal));
}
}
painter.restore();
painter.end();
//保存绘图
_pixmap.save("dashBoard.png");
}
void DashBoard::drawPointer(QPainter& painter){
QPointF p1(-0.02*_radius,-0.15*_radius);
QPointF p2(0,-0.16*_radius);
QPointF p3(0.02*_radius,-0.15*_radius);
QPointF p4(0,0.8*_radius);
QPolygonF polygon;
polygon<<p1<<p2<<p3<<p4<<p1;
double nowAngle=_startAngle+(double)(_curVal-_minVal)/(_maxVal-_minVal)*(360-2*_startAngle);
painter.save();
painter.translate(_x+_radius,_y+_radius);
painter.rotate(nowAngle);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(0xdd0615));
painter.drawPolygon(polygon);
painter.restore();
}
效果
简单的绘制了一下,没有完全实现上上图效果。
可以看到,在编译文件中生成了一个png格式的图片。
打开后的效果:
可以看到打开这个图片后,除了反应数值的指针没有绘制,其他内容都绘制在了png图片中。当然,也可以不保存绘图,将绘制好不用频繁绘制的部分保存在内存中。即注释掉_pixmap.save("dashBoard.png");