Qt底层原理:深入解析QWidget的绘制技术细节(1)

在Qt5中,QWidget的绘制流程比较分散,网上介绍的文章也很少,因此写一篇文章总结记录一下这部分的知识点。

笔者使用的是Qt5.15.2的源码。

基本的绘制流程:从update到合成

  1. 更新请求(Invalidate):
    当一个QWidget需要被重绘时(比如大小改变、数据更新等),会调用update()方法来标记该widget为需要重绘。update一般会到repaintManager->markDirty,如果当前正在绘制,则通过事件QUpdateLaterEvent进行重绘。这部分逻辑代码如下:
    在这里插入图片描述

  2. 重绘区域计算(Dirty Region Calculation):

    • Qt有一个优化机制,它会合并多个重绘请求以减少重绘的次数和区域。重绘区域的计算由Qt的QWidgetRepaintManager负责,该系统维护了一个脏区域(dirty regions),这是所有需要重绘的区域的集合。主要逻辑在QWidgetRepaintManager::markDirty。这部分逻辑稍微复杂,但不是重点,感兴趣的读者可以自行翻阅源码,此处不再列出。
  3. 事件处理(Event Processing):

    • 在QWidgetRepaintManager::sendUpdateRequest,会生成一个QEvent::UpdateRequest的事件,即使指定了UpdateNow,也会根据这次更新是否距离上次更新大于60fps而降低这次绘制的优先级。 这部分逻辑代码如下:在这里插入图片描述
  4. 事件循环(Event Loop):
    Qt的事件循环在QCoreApplication::exec()调用后运行,负责处理事件队列中的事件。对于绘制事件,事件循环会传递给QWidget的event()方法。这部分不是本文章重点,不列出详细细节。

  5. 事件处理(Event Handling):
    QWidget的event()方法会检查事件的类型。如果是绘制事件QEvent::UpdateRequest或者QEvent::UpdateLater,会转调到QWidgetPrivate::paintOnScreen函数,接着使用QWidgetRepaintManager类提供的功能,转调到每个Widget::paintEvent函数。 这部分逻辑代码如下:在这里插入图片描述

  6. 绘制逻辑paintAndFlush:这部分是本文的重点,也比较复杂,在后文详细展开。

  7. 绘制(Painting):
    paintEvent()方法中,一般使用QPainter对象,它是Qt中负责绘制的类。QPainter可以绘制各种图形元素,如文本、线条、形状等。

  8. 绘图设备(Paint Device):
    QPainter对象会被绑定到一个绘图设备(QPaintDevice),比如QWidget本身,或者一个QPixmapQImageQPicture等。QWidget通过其paintEngine()方法提供了一个QPaintEngine对象,这是实际进行绘制操作的底层接口。

  9. 绘图引擎(Paint Engine):
    QPaintEngine是一个抽象基类,它定义了绘图操作的接口。Qt提供了多种绘图引擎,比如QRasterPaintEngineQOpenGLPaintEngine等,具体使用哪个引擎取决于QWidget的绘制设备以及平台特性。

在源代码层面,以下是几个关键类和它们在绘制流程中的作用:

  • QWidget: 作为所有UI组件的基类,管理绘制和事件。
  • QPaintEvent: 继承自QEvent,封装了绘制事件的信息。
  • QPainter: 提供了一组API来执行绘制操作。
  • QPaintDevice: 是一个抽象类,QWidget和其他一些类比如QImage、QPixmap都是这个类的子类,用于表示可以被绘制的对象。
  • QPaintEngine: 抽象基类,定义了底层绘图操作的接口。
  • QWidgetRepaintManager:主要绘制流程的管理类。

Qt提供了QWidget::setUpdatesEnabled()方法,允许开发者禁用或启用控件的更新。这可以用来在批量修改控件时暂时禁用更新,以避免不必要的重绘。

例如,QPushButton的paintEvent堆栈如下:在这里插入图片描述

绘制半透明的控件:父子Widget绘制细节

在Qt中,重绘一个子控件默认不会导致父控件重绘。但是,如果子控件是半透明的(具有alpha通道不是完全不透明的颜色),那么会导致父控件重绘内容作为背景来正确地绘制子控件。
这部分的逻辑比较复杂,核心逻辑在QWidgetRepaintManager::paintAndFlush里,这个函数的源码不在此贴出,但是分析这个函数内部的主要逻辑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值