Qt GUI缓存实现


Qt GUI高性能缓存实现核心思想 (双缓冲技术)

一、 为什么需要缓存?
  • 核心 : paintEvent 函数在每次界面刷新时都会被调用。如果我们在 paintEvent 内部执行复杂的、耗时的绘制操作(如加载大图片、绘制大量图元、计算复杂渐变等),当刷新频率很高时(如动画、拖动),就会导致大量的CPU资源消耗,从而引起界面卡顿。
二、 解决方案的核心思想:"空间换时间"
  • 核心思想 : 双缓冲 (Double Buffering) 。用一块额外的内存(QPixmap 对象),来一次性地存储昂贵绘制操作的结果。之后每次刷新,而是直接将这个已经画好的结果"贴"到屏幕上。
  • 本质 : 将多次的"慢速创作 "(paintEvent里的复杂绘制)转变为一次"慢速创作 " + 多次的"极速复制 "(drawPixmap)。
三、 实现缓存的"三步曲"

第一步:准备"后台画布" (成员变量)

  1. 在自定义控件的头文件 (.h) 中,声明一个 QPixmap 类型的成员变量,作为我们的"后台画布"或"缓存"。

    cpp 复制代码
    private:
        QPixmap m_cache;

第二步:创建"创作函数" (更新缓存)

  1. 在源文件 (.cpp) 中,创建一个私有的辅助函数,专门负责在"后台画布"上进行所有昂贵的绘制操作。

    cpp 复制代码
    void CustomWidget::updateCache()
    {
        // 1. 初始化画布:根据当前控件大小创建一张等大的Pixmap
        m_cache = QPixmap(size());
        // 2. 【关键】清空画布:用透明色填充,确保画布是干净的,并为不规则图形提供透明背景
        m_cache.fill(Qt::transparent);
    
        // 3. 创建画家,指定在"后台画布"上作画
        QPainter painter(&m_cache);
        painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿
    
        // 4. 【核心】在这里执行所有昂贵的、复杂的绘制指令...
        // painter.drawPixmap(...);
        // painter.drawRoundedRect(...);
        // ...
    }

第三步:改造 paintEvent (执行"印刷")

  1. 重写 paintEvent 函数,将其职责从"创作"转变为"印刷"。

    cpp 复制代码
    void CustomWidget::paintEvent(QPaintEvent *event)
    {
        QPainter painter(this); // 创建画家,指定在"屏幕"(this)上作画
    
        // 【核心】不再执行任何复杂绘制,只做一步:
        // 把已经画好的"后台画布" (m_cache),瞬间"贴"到屏幕的左上角 (0, 0)。
        painter.drawPixmap(0, 0, m_cache);
    }
四、 触发缓存更新的"两大时机"
  1. 时机一:控件尺寸固定不变时

    • 策略 : 在构造函数 的末尾,调用一次 updateCache() 即可。

    • 原理: 尺寸永远不变,只在控件诞生时"创作"一次,之后就可以一劳永逸地"印刷"了。

    • 示例 :

      cpp 复制代码
      CustomInputWidget::CustomInputWidget(...) {
          setFixedSize(...);
          // ...
          updateCache(); // 在构造函数里一次性生成缓存
      }
  2. 时机二:控件尺寸可能发生变化时

    • 策略 : 重写 resizeEvent(QResizeEvent *event) 事件处理器,在函数体内调用 updateCache()

    • 原理 : resizeEvent 会在控件的尺寸每次发生变化时被自动调用。我们必须在这个时机废弃旧的、尺寸不匹配的缓存,并根据新尺寸重新"创作"一份新的缓存。

    • 示例 :

      cpp 复制代码
      void FramedWidget::resizeEvent(QResizeEvent *event) {
          updateCache(); // 尺寸变了,重建缓存
          QWidget::resizeEvent(event); // 调用父类实现是好习惯
      }
    • 补充 : 在这种情况下,我们还需要在 paintEvent 的开头加一个保护,防止在第一次显示、resizeEvent 还未被调用时缓存为空。

      cpp 复制代码
      void FramedWidget::paintEvent(...) {
          if (m_cache.isNull()) {
              updateCache(); // 如果缓存是空的,先创建一次
          }
          // ... 贴图 ...
      }

相关推荐
萤丰信息8 分钟前
技术赋能安全:智慧工地构建城市建设新防线
java·大数据·开发语言·人工智能·智慧城市·智慧工地
Pocker_Spades_A30 分钟前
飞算JavaAI家庭记账系统:从收支记录到财务分析的全流程管理方案
java·开发语言
CHEN5_022 小时前
【Java基础常见辨析】重载与重写,深拷贝与浅拷贝,抽象类与普通类
java·开发语言
Despacito0o2 小时前
C语言基础:变量与进制详解
java·c语言·开发语言
nightunderblackcat2 小时前
进阶向:人物关系三元组,解锁人物关系网络的钥匙
开发语言·python·开源·php
科大饭桶3 小时前
C++入门自学Day11-- String, Vector, List 复习
c语言·开发语言·数据结构·c++·容器
范范之交3 小时前
JavaScript基础语法two
开发语言·前端·javascript
Felven3 小时前
C. Game of Mathletes
c语言·开发语言
点云SLAM4 小时前
C++中内存池(Memory Pool)详解和完整示例
开发语言·c++·内存管理·内存池·new/delete·malloc/free
程高兴4 小时前
遗传算法求解冷链路径优化问题matlab代码
开发语言·人工智能·matlab