qt 双缓冲案例对比

news2025/6/10 6:11:18

双缓冲

1.双缓冲原理

单缓冲:在paintEvent中直接绘制到屏幕,绘制过程被用户看到
双缓冲:先在redrawBuffer绘制到缓冲区,然后一次性显示完整结果
代码结构
单缓冲:所有绘制逻辑在paintEvent中
双缓冲:绘制逻辑分离到redrawBuffer,paintEvent仅负责显示缓冲区
性能影响
单缓冲:每次更新需要重复计算和绘制
双缓冲:可以优化为只在必要时重绘缓冲区(如数据变化时)

减少的时间是重绘的那部分时间,单缓冲是逐个绘制,双缓冲是整体绘制。

测试案例

1.Qt 实现绘制 5000 个小球移动,单缓冲和双缓冲效果对比

双缓冲和单缓冲效果对比

2.代码

关键代码对比:
单缓冲绘制: paintEvent 中通过 painter 绘制每一个 circle,一次 paintEvent 绘制 5000 次
双缓冲绘制: paintEvent 中直接绘制提前赋好像素值的成员变量 buffer 中的像素

doublebufferwidget.h

#include <QWidget>
#include <QPainter>
#include <QTimer>
#include <QPixmap>
#include <QPointF>
#include <QVector>
#include <cmath>
#include <QTime>
#include <QDebug>


class DoubleBufferWidget : public QWidget {
    Q_OBJECT
public:
    DoubleBufferWidget(QWidget *parent = nullptr) : QWidget(parent) {
        // 初始化缓冲区
        setWindowTitle("DoubleBuffer");
        buffer = QPixmap(size());
        buffer.fill(Qt::white);

        // 初始化动画数据
        for (int i = 0; i < 5000; ++i) {
            circles.append({
                QPointF(rand() % width(), rand() % height()),
                5.f + rand() % 10,
                QColor(rand() % 256, rand() % 256, rand() % 256),
                QPointF((rand() % 100 - 50) / 100.0, (rand() % 100 - 50) / 100.0)
            });
        }

        // 设置定时器更新动画
        QTimer *timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, &DoubleBufferWidget::updateAnimation);
        timer->start(16);
    }

    void resizeEvent(QResizeEvent *event) override {
        buffer = QPixmap(size());
        buffer.fill(Qt::white);
        QWidget::resizeEvent(event);
    }

    void paintEvent(QPaintEvent *event) override {
        QTime doublePaint;

        doublePaint.start();

        Q_UNUSED(event);
        QPainter painter(this);
        painter.drawPixmap(0, 0, buffer);

        qInfo() << "void paintEvent(QPaintEvent *event) doublePaint cost times:" << doublePaint.elapsed() << "ms";
    }

private slots:
    void updateAnimation() {
        // 更新所有圆形位置
        for (auto &circle : circles) {
            circle.pos += circle.velocity;

            if (circle.pos.x() - circle.radius < 0 || circle.pos.x() + circle.radius > width())
                circle.velocity.setX(-circle.velocity.x());
            if (circle.pos.y() - circle.radius < 0 || circle.pos.y() + circle.radius > height())
                circle.velocity.setY(-circle.velocity.y());
        }

        // 重新绘制到缓冲区
        redrawBuffer();

        update();
    }

private:
    struct Circle {
        QPointF pos;
        qreal radius;
        QColor color;
        QPointF velocity;
    };

    QPixmap buffer;
    QVector<Circle> circles;

    void redrawBuffer() {
        buffer.fill(Qt::white);
        QPainter bufferPainter(&buffer);
        bufferPainter.setRenderHint(QPainter::Antialiasing);

        // 模拟长时间绘制过程
        for (int i = 0; i < 1000; ++i) {
            double temp = std::sin(i);
            Q_UNUSED(temp);
        }

        // 绘制所有圆形
        for (const auto &circle : circles) {
            bufferPainter.setPen(Qt::NoPen);
            bufferPainter.setBrush(circle.color);
            bufferPainter.drawEllipse(circle.pos, circle.radius, circle.radius);
        }
    }
};

singlebufferwidget.h

#include <QWidget>
#include <QTime>
#include <QPainter>
#include <QTimer>
#include <math.h>
#include <qmath.h>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class SingleBufferWidget; }
QT_END_NAMESPACE

class SingleBufferWidget : public QWidget {
    Q_OBJECT
public:
    SingleBufferWidget(QWidget *parent = nullptr) : QWidget(parent) {
        // 初始化动画数据
        for (int i = 0; i < 5000; ++i) {
            circles.append({
                QPointF(rand() % width(), rand() % height()),
                5.f + rand() % 10,
                QColor(rand() % 256, rand() % 256, rand() % 256),
                QPointF((rand() % 100 - 50) / 100.0, (rand() % 100 - 50) / 100.0)
            });
        }

        // 设置定时器更新动画
        QTimer *timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, &SingleBufferWidget::updateAnimation);
        timer->start(16);
    }

    void paintEvent(QPaintEvent *event) override {

        QTime SingpaintTimer;
        SingpaintTimer.start();

        Q_UNUSED(event);
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);

        // 模拟长时间绘制过程
        for (int i = 0; i < 1000; ++i) {
            // 这个循环会延长绘制时间,加剧闪烁现象
            double temp = std::sin(i);
            Q_UNUSED(temp);
        }

        // 绘制所有圆形
        for (const auto &circle : circles) {
            painter.setPen(Qt::NoPen);
            painter.setBrush(circle.color);
            painter.drawEllipse(circle.pos, circle.radius, circle.radius);
        }

        qInfo() << "void paintEvent(QPaintEvent *event) SingpaintTimer cost times:" << SingpaintTimer.elapsed() << "ms";
    }

private slots:
    void updateAnimation() {
        // 更新所有圆形位置
        for (auto &circle : circles) {
            circle.pos += circle.velocity;

            if (circle.pos.x() - circle.radius < 0 || circle.pos.x() + circle.radius > width())
                circle.velocity.setX(-circle.velocity.x());
            if (circle.pos.y() - circle.radius < 0 || circle.pos.y() + circle.radius > height())
                circle.velocity.setY(-circle.velocity.y());
        }

        update();
    }

private:
    struct Circle {
        QPointF pos;
        qreal radius;
        QColor color;
        QPointF velocity;
    };

    QVector<Circle> circles;
};

3.工程文件资源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2406286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实现p2p的webrtc-srs版本

1. 基本知识 1.1 webrtc 一、WebRTC的本质&#xff1a;实时通信的“网络协议栈”类比 将WebRTC类比为Linux网络协议栈极具洞察力&#xff0c;二者在架构设计和功能定位上高度相似&#xff1a; 分层协议栈架构 Linux网络协议栈&#xff1a;从底层物理层到应用层&#xff08;如…

第2篇:BLE 广播与扫描机制详解

本文是《BLE 协议从入门到专家》专栏第二篇,专注于解析 BLE 广播(Advertising)与扫描(Scanning)机制。我们将从协议层结构、广播包格式、设备发现流程、控制器行为、开发者 API、广播冲突与多设备调度等方面,全面拆解这一 BLE 最基础也是最关键的通信机制。 一、什么是 B…

开源 vGPU 方案:HAMi,实现细粒度 GPU 切分

本文主要分享一个开源的 GPU 虚拟化方案&#xff1a;HAMi&#xff0c;包括如何安装、配置以及使用。 相比于上一篇分享的 TimeSlicing 方案&#xff0c;HAMi 除了 GPU 共享之外还可以实现 GPU core、memory 得限制&#xff0c;保证共享同一 GPU 的各个 Pod 都能拿到足够的资源。…

盲盒一番赏小程序:引领盲盒新潮流

在盲盒市场日益火爆的今天&#xff0c;如何才能在众多盲盒产品中脱颖而出&#xff1f;盲盒一番赏小程序给出了答案&#xff0c;它以创新的玩法和优质的服务&#xff0c;引领着盲盒新潮流。 一番赏小程序的最大特色在于其独特的赏品分级制度。赏品分为多个等级&#xff0c;从普…

边缘计算设备全解析:边缘盒子在各大行业的落地应用场景

随着工业物联网、AI、5G的发展&#xff0c;数据量呈爆炸式增长。但你有没有想过&#xff0c;我们生成的数据&#xff0c;真的都要发回云端处理吗&#xff1f;其实不一定。特别是在一些对响应时间、网络带宽、数据隐私要求高的行业里&#xff0c;边缘计算开始“火”了起来&#…

Linux实现线程同步的方式有哪些?

什么是线程同步&#xff1f; 想象一下超市收银台&#xff1a;如果所有顾客&#xff08;线程&#xff09;同时挤向同一个收银台&#xff08;共享资源&#xff09;&#xff0c;场面会一片混乱。线程同步就是给顾客们发"排队号码牌"&#xff0c;确保&#xff1a; 有序访…

python学习day39

图像数据与显存 知识点回顾 1.图像数据的格式&#xff1a;灰度和彩色数据 2.模型的定义 3.显存占用的4种地方 a.模型参数梯度参数 b.优化器参数 c.数据批量所占显存 d.神经元输出中间状态 4.batchisize和训练的关系 import torch import torchvision import torch.nn as nn imp…

年度峰会上,抖音依靠人工智能和搜索功能吸引广告主

上周早些时候举行的第五届年度TikTok World产品峰会上&#xff0c;TikTok推出了一系列旨在增强该应用对广告主吸引力的功能。 新产品列表的首位是TikTok Market Scope&#xff0c;这是一个全新的分析平台&#xff0c;为广告主提供整个考虑漏斗的全面视图&#xff0c;使他们能够…

如何使用CodeRider插件在IDEA中生成代码

一、环境搭建与插件安装 1.1 环境准备 名称要求说明操作系统Windows 11JetBrains IDEIntelliJ IDEA 2025.1.1.1 (Community Edition)硬件配置推荐16GB内存50GB磁盘空间 1.2 插件安装流程 步骤1&#xff1a;市场安装 打开IDEA&#xff0c;进入File → Settings → Plugins搜…

电脑定时关机工具推荐

软件介绍 本文介绍一款轻量级的电脑自动关机工具&#xff0c;无需安装&#xff0c;使用简单&#xff0c;可满足定时关机需求。 工具简介 这款关机助手是一款无需安装的小型软件&#xff0c;文件体积仅60KB&#xff0c;下载后可直接运行&#xff0c;无需复杂配置。 使用…

前端异步编程全场景解读

前端异步编程是现代Web开发的核心&#xff0c;它解决了浏览器单线程执行带来的UI阻塞问题。以下从多个维度进行深度解析&#xff1a; 一、异步编程的核心概念 JavaScript的执行环境是单线程的&#xff0c;这意味着在同一时间只能执行一个任务。为了不阻塞主线程&#xff0c;J…

分布式光纤声振传感技术原理与瑞利散射机制解析

分布式光纤传感技术&#xff08;Distributed Fiber Optic Sensing&#xff0c;简称DFOS&#xff09;作为近年来迅速发展的新型感知手段&#xff0c;已广泛应用于边界安防、油气管道监测、结构健康诊断、地震探测等领域。其子类技术——分布式光纤声振传感&#xff08;Distribut…

RocketMQ 客户端负载均衡机制详解及最佳实践

延伸阅读&#xff1a;&#x1f50d;「RocketMQ 中文社区」 持续更新源码解析/最佳实践&#xff0c;提供 RocketMQ 专家 AI 答疑服务 前言 本文介绍 RocketMQ 负载均衡机制&#xff0c;主要涉及负载均衡发生的时机、客户端负载均衡对消费的影响&#xff08;消息堆积/消费毛刺等…

【SSM】SpringMVC学习笔记7:前后端数据传输协议和异常处理

这篇学习笔记是Spring系列笔记的第7篇&#xff0c;该笔记是笔者在学习黑马程序员SSM框架教程课程期间的笔记&#xff0c;供自己和他人参考。 Spring学习笔记目录 笔记1&#xff1a;【SSM】Spring基础&#xff1a; IoC配置学习笔记-CSDN博客 对应黑马课程P1~P20的内容。 笔记2…

C++课设:实现本地留言板系统(支持留言、搜索、标签、加密等)

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 专栏介绍&#xff1a;《编程项目实战》 目录 一、项目功能概览与亮点分析1. 核心功能…

【见合八方平面波导外腔激光器专题系列】用于干涉光纤传感的低噪声平面波导外腔激光器2

----翻译自Mazin Alalus等人的文章 摘要 1550 nm DWDM 平面波导外腔激光器具有低相位/频率噪声、窄线宽和低 RIN 等特点。该腔体包括一个半导体增益芯片和一个带布拉格光栅的平面光波电路波导&#xff0c;采用 14 引脚蝶形封装。这种平面波导外腔激光器设计用于在振动和恶劣的…

Xcode 16.2 版本 pod init 报错

Xcode 版本升级到 16.2 后&#xff0c;项目执行 pod init 报错&#xff1b; ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchron…

timestamp时间戳转换工具

作为一名程序员&#xff0c;一款高效的 在线转换工具 &#xff08;在线时间戳转换 计算器 字节单位转换 json格式化&#xff09;必不可少&#xff01;https://jsons.top 排查问题时非常痛的点: 经常在秒级、毫秒级、字符串格式的时间单位来回转换&#xff0c;于是决定手撸一个…

数据库管理与高可用-MySQL故障排查与生产环境优化

目录 #1.1MySQL单案例故障排查 1.1.1MySQL常见的故障排查 1.1.2MySQL主从故障排查 #2.1MySQL优化 2.1.1硬件方面的优化 2.1.2进程方面的优化 #3.1MySQL存储引擎 3.1.1 MyISAM存储引擎 3.1.2 InnoDB存储引擎 1.1MySQL单案例故障排查 1.1.1MySQL常见的故障排查 &#xff08;1&…

LangChain + LangSmith + DeepSeek 入门实战:构建代码生成助手

本文基于 Jupyter Notebook 实践代码&#xff0c;结合 LangChain、LangSmith 和 DeepSeek 大模型&#xff0c;手把手演示如何构建一个代码生成助手&#xff0c;并实现全流程追踪与优化。 一、环境准备与配置 1. 安装依赖 pip install langchain langchain_openai2. 设置环境变…