【Qt】2D绘图之双缓冲绘图

00. 目录

01. 概述

所谓的双缓冲绘图的概念。双缓冲(double-buffers)绘图,就是在进行绘制时,先将所有内容都绘制到一个绘图设备(如QPixmap)上,然后再将整个图像绘制到部件上显示出来。使用双缓冲绘图可以避免显示时的闪烁现象。从Qt 4.0开始,QWidget部件的所有绘制都自动使用了双缓冲,所以一般没有必要在paintEvent()函数中使用双缓冲代码来避免闪烁。

​ 虽然在一般的绘图中无需手动使用双缓冲绘图,不过要想实现一些绘图效果,还是要借助于双缓冲的概念。比如这个程序里,我们要实现使用鼠标在界面上绘制一个任意大小的矩形。这里需要两张画布,它们都是QPixmap实例,其中一个tempPix用来作为临时缓冲区,当鼠标正在拖动矩形进行绘制时,将内容先绘制到tempPix上,然后将tempPix绘制到界面上;而另一个pix作为缓冲区,用来保存已经完成的绘制。当松开鼠标完成矩形的绘制后,则将tempPix的内容复制到pix上。为了绘制时不显示拖影,而且保证以前绘制的内容不消失,那么在移动鼠标过程中,每绘制一次,都要在绘制这个矩形的原来的图像上进行绘制,所以需要在每次绘制tempPix之前,先将pix的内容复制到tempPix上。因为这里有两个QPixmap对象,也可以说有两个缓冲区,所以称之为双缓冲绘图。

02. 开发环境

Windows系统:Windows10

Qt版本:Qt5.15或者Qt6

03. 绘制矩形

3.1 在涂鸦板的程序的基础之上继续更改

//绘图事件
void Dialog::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    int x, y, w, h;
    x = lastPoint.x();
    y = lastPoint.y();
    w = endPoint.x() - x;
    h = endPoint.y() - y;

    painter.drawRect(x, y, w, h);
}

运行结果
在这里插入图片描述

3.2 上面已经可以拖出一个矩形了,但是这样直接在窗口上绘图,以前画的矩形是不能保存下来的。所以我们下面加入画布,在画布上进行绘图。将paintEvent()函数更改如下:

//绘图事件
void Dialog::paintEvent(QPaintEvent *)
{

    int x, y, w, h;
    x = lastPoint.x();
    y = lastPoint.y();
    w = endPoint.x() - x;
    h = endPoint.y() - y;

    QPainter p1(&pix);
    p1.drawRect(x, y, w, h);

    QPainter painter(this);
    painter.drawPixmap(0, 0, pix);
}

这里就是将图形先绘制在了画布上,然后将画布绘制到窗口上。我们运行程序,然后使用鼠标拖出一个矩形,发现出现了很多重影,效果如下图所示。
在这里插入图片描述

大家可以尝试分别快速拖动鼠标和慢速拖动鼠标来绘制矩形,结果会发现,拖动速度越快,重影越少。其实,在我们拖动鼠标的过程中,屏幕已经刷新了很多次,也可以理解为paintEvent()函数执行了多次,每执行一次就会绘制一个矩形。知道了原因,就可以想办法来避免这个问题发生了。

04. 双缓冲绘图

4.1 我们再添加一个辅助画布,如果正在绘图,也就是鼠标按键还没有释放的时候,就在这个辅助画布上绘图,只有当鼠标按键释放的时候,才在真正的画布上绘图。

首先在dialog.h文件中添加两个私有变量

    //临时画布
    QPixmap tmpPix;
    //标志是否正在绘图
    bool isDrawing;

在构造函数初始化

    isDrawing = false;

4.2 更改paintEvent()函数

//绘图事件
void Dialog::paintEvent(QPaintEvent *)
{

    int x, y, w, h;
    x = lastPoint.x();
    y = lastPoint.y();
    w = endPoint.x() - x;
    h = endPoint.y() - y;

    QPainter painter(this);

    //如果正在绘图,就在临时画布上面绘制
    if (isDrawing)
    {
        tmpPix = pix;

        QPainter p1(&tmpPix);

        p1.drawRect(x, y, w, h);
        painter.drawPixmap(0, 0, tmpPix);
    }
    else
    {
        QPainter p1(&pix);
        p1.drawRect(x, y, w, h);
        painter.drawPixmap(0, 0, pix);
    }
}

4.3 更改鼠标按下事件处理函数和鼠标释放事件处理函数的内容:

//鼠标左键按下事件
void Dialog::mousePressEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        lastPoint = e->pos();

        //正在绘图
        isDrawing = true;
    }

    endPoint = lastPoint;
}


//鼠标释放事件
void Dialog::mouseReleaseEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        endPoint = e->pos();

        //结束绘图
        isDrawing = false;
        update();
    }

}

当鼠标左键按下时我们标记正在绘图,当按键释放时我们取消正在绘图的标记。现在运行程序,已经可以实现正常的绘图了。效果如下图所示。
在这里插入图片描述

05. 附录

源码下载:【Qt】2D绘图之双缓冲绘图.rar

© 版权声明
THE END
喜欢就支持一下吧
点赞53 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容