当前位置: 首页 > news >正文

qt 双缓冲案例对比

双缓冲

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_NAMESPACEclass 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.工程文件资源

相关文章:

  • Vue 自动导入函数和变量插件 unplugin-auto-import
  • Vue动态/异步组件
  • Vue3中的computer和watch
  • tauri项目,如何在rust端读取电脑环境变量
  • 背包问题双雄:01 背包与完全背包详解(Java 实现)
  • React hook之useRef
  • 什么是Java bean的依赖注入
  • Vue3 PC端 UI组件库我更推荐Naive UI
  • Docker环境下FileRise私有云盘在飞牛NAS的部署与穿透实践
  • 《前端面试题:ES6新特性》
  • 行列视:企业数据分析新时代的利器(一)——深度解读与应用场景分析
  • 第2课 SiC MOSFET与 Si IGBT 静态特性对比
  • HarmonyOS运动开发:打造你的专属运动节拍器
  • Excel处理控件Aspose.Cells教程:在Excel 文件中创建、操作和渲染时间线
  • boost::filesystem::path文件路径使用详解和示例
  • Spring MVC执行流程简介
  • 玩转 Skia 的颜色
  • LeetCode - 543. 二叉树的直径
  • 如何开发ONLYOFFICE协作空间插件:完整教程
  • 大学生职业发展与就业创业指导教学评价
  • 网站建设教程视频/百度产品推广怎么收费
  • 有没有专门做av中文的网站/石家庄自动seo
  • 互联网保险新规/武汉百度网站优化公司
  • 专用主机网站建设/如何进行网络推广
  • 中国建设银行手机版网站首页/百度下载安装到手机
  • 北京网站制作建设公司/大型网站制作