Qt 中的重载 paintEvent 方法:示例和解析

学了qss之后会了一些美化,但是发现还有很多qss没法实现,那么这个文章就适合你了

当然不喜欢使用qss的比如我,也可以通过该文章快速学习通过重载paintEvent来绘制你想要的控件

paintEvent是一个虚函数,需要子类进行重载才能实现自定义绘制

paintEvent是一个绘制函数,会在调用后绘制图形

示例

这里先给出一个示例,然后来逐步解析这个示例让你理解

1
2
3
4
5
6
7
8
9
10
void QtWidget::paintEvent(QPaintEvent* Event) {
QWidget::paintEvent(Event);
QPainter painter(this);
QPen pen(QColor("#b883a0"));
QBrush brush(QColor("#b883a0"));
painter.setPen(pen);
painter.setBrush(brush);
painter.drawEllipse(QRect(0, 0, width(), height()));
painter.end();
}

这样一段简单的代码你就得到了一个圆

接下来逐步解析这段代码

1
QWidget::paintEvent(Event);

这段代码,是调用父类的绘制函数,来绘制一些默认和设置过后的图形

1
QPainter painter(this);

声明了一个QPainter对象,它是一个画笔,可以用来绘制你想在控件上绘制的图形

QPainter的构造函数需要传入QPainterDevice,你不知道这是啥,没关系,你只要知道Qt中所有的控件都是它的子类就行了

这里我们传入了当前对象的指针,作为这个QPainter的绘图设备

1
QPen pen(QColor("#b883a0"));

这里传入了一个QColor也就是颜色去构造pen,QPen是一个画笔,当然它和前面的QPainter不一样,它储存着画笔的信息,是用来画图形的边框的

1
QBrush brush(QColor("#b883a0"));

QBrush是一个画刷,它储存的是填充封闭图形的画刷的信息

1
2
painter.setPen(pen);
painter.setBrush(brush);

这里应该不用过多解释了,将pen和brush分别传入painter中

1
painter.drawEllipse(QRect(0, 0, width(), height()));

painter对象调用了drawEllipse函数绘制了一个圆,传入了一个QRect,QRect构造函数中的参数分别是(x轴坐标,y轴坐标,控件的宽,控件的高)

更多的绘制函数,可以参阅Qt文档 里面有解释

1
painter.end();

最后调用end函数,结束绘画,当然不止end函数能结束绘制,销毁painter也是会结束绘制的

实现了这么个窗口

相信到这里已经明白paintEvent函数后需要干什么和怎样绘制自己想要的控件了


解释部分

上文没有讲到的类

QFont

这是一个字体类,里面储存了字体的信息,传入QPainter对象后,会在调用drawText时被读取

paintEvent的触发时机和方式

接下来说paintEvent会在什么时候被调用

  1. show()
    这是一个显示函数,它会调用paintEvent来绘制图形,注意,只有在被隐藏之后调用show()才会调用paintEvent来绘制,已经调用一次show()且没有调用hide()隐藏是不会调用paintEvent来重绘的,如果状态改变了需要重绘可以看下文的两个函数
  2. updata()
    这是一个重绘函数,它不会立即调用paintEvent来重绘图形,它会返回主事件循环时安排一个绘制事件进行处理,Qt会对其优化速度和减少闪烁,updata()在调用之后通常只会调用一次paintEvent,当然也会有例外,见下文给出的文章
  3. repaint()
    这是一个重绘函数,它会直接调用paintEvent来重绘图形
    这个函数有风险,可能会一直递归循环调用父类的paintEvent,最好是在需要快速重绘的时候,比如动画等等,再去调用
  4. 窗口大小修改后
    比如拉动窗口边缘去改变窗口大小的时候就会直接调用paintEvent函数去重绘图形

Qt的文档给出了说明:

Warning: If you call repaint() in a function which may itself be called from paintEvent(), you may get infinite recursion. The update() function never causes recursion.

自定义绘制paintEvent方法导致的递归循环 文章有解释,可以去看看

优缺点

优点当然是自定义程度高,可以绘制一切你想绘制的图形

缺点对于小白来说,复用性不高,可能这个类到另外一个场景就要再写一个,当然使用一些时间后就会知道如何增加复用性

去增加几个成员变量,在使用paintEvent去绘制时根据你的成员变量去绘制也能提高复用性

比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// QtWidget.h
QString TextColor;
QString Text;
void setColor(QString color);
void setText(QString text);
void paintEvent(QPaintEvent* Event);
//QtWidget.cpp
void QtWidget::paintEvent(QPaintEvent* Event) {
QPainter painter(this);
painter.setFont(QFont("宋体",15));
painter.setPen(QColor("#f6cf9b"));
painter.setBrush(QColor("#f6cf9b"));
painter.drawText(0,0,Text);
}

这里面就定义了两个成员变量,在paintEvent中绘制,增加了复用性

本文就到这里了,如果有什么地方写错了,可以在下面提出来