QT—双缓冲绘图

x33g5p2x  于2022-07-04 转载在 其他  
字(2.5k)|赞(0)|评价(0)|浏览(266)

所谓双缓冲(double-buffers)绘图,就是在进行绘制时,先将所有内容都绘制到一个绘图设备(如 QPixmap)上,然后再将整个图像绘制到部件上显示出来

使用双缓冲绘图可以避免显示时的闪烁现象
从Qt 4.0开始,QWidget部件的所有绘制都自动使用了双缓冲,所以一般没有必要在 paintEvent()函数中使用双缓冲代码来避免闪烁。

虽然在一般的绘图中无须手动使用双缓冲绘图,不过要想实现一些绘图效果,还是要借助于双缓冲的概念。

下面的程序实现使用鼠标在界面上绘制一个任意大小的矩形的功能

这里需要两张画布,它们都是QPixmap实例,其中一个tempPix用来作为临时缓冲区,当鼠标正在拖动矩形进行绘制时,将内容先绘制到tempPix上,然后将tempPix绘制到界面上;而另一个pix作为缓冲区,用来保存已经完成的绘制

当松开鼠标完成矩形的绘制后,则将tempPix的内容复制到pix上。

为了绘制时不显示拖影,在移动鼠标过程中,每绘制一次都要在刚开始绘制这个矩形的图像上进行绘制,所以需要在每次绘制tempPix之前,先将pix的内容复制到tempPix 上

新建Qt Widgets应用,项目名称为mydoublebuffers,基类选择QWidget,类名为 Widget。
建立完成后,在 widget.h文件中添加如下内容:

private:
    Ui::Widget *ui;
    // 缓冲区
    QPixmap pix;
    // 临时缓冲区
    QPixmap tempPix;
    QPoint startPoint;
    QPoint endPoint;
    // 是否正在绘图的标志
    bool isDrawing;

protected:
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    void paintEvent(QPaintEvent *event);

在这里定义了方法和绘画设备等
然后到widget.cpp文件中,先添加头文件

#include <QMouseEvent>
#include <QPainter>

然后到构造函数里面添加一些变量并初始化

pix = QPixmap(400, 300);
    pix.fill(Qt::white);
    tempPix = pix;
    isDrawing = false;

pix是400,300尺寸的QPixmap设备,白色,复制给tempPix防止重影,isDrawing 关闭

下面是鼠标事件处理函数:

void Widget::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton) {
        // 当鼠标左键按下时获取当前位置作为矩形的开始点
        startPoint = event->pos();
        // 标记正在绘图
        isDrawing = true;
    }
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
    if(event->buttons() & Qt::LeftButton) {
        // 当按着鼠标左键进行移动时,获取当前位置作为结束点,绘制矩形
        endPoint = event->pos();
        // 将缓冲区的内容复制到临时缓冲区,这样进行动态绘制时,
        // 每次都是在缓冲区图像的基上进行绘制,就不会产生拖影现象了
        tempPix = pix;
        // 更新显示
        update();
    }
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton) {
        // 当鼠标左键松开时,获取当前位置为结束点,完成矩形绘制
        endPoint = event->pos();
        // 标记已经结束绘图
        isDrawing = false;
        update();
    }
}

这里在鼠标按下事件处理函数中获取了要绘制矩形左上角的位置,然后标记正在绘制矩形

在鼠标移动事件处理函数中获取了要绘制矩形的右下角的位置,然后动态绘制矩形,这里为了不会绘制出一堆小矩形而产生所谓的拖影现象,就要在绘制临时缓冲区前,将缓冲区的内容复制到临时缓冲区中。这样每次都是在缓冲区图像的基础上进行绘制的,所以不会产生拖影现象

最后在鼠标按键释放事件处理函数中,获取矩形的右下角坐标,标记已经结束绘制。

下面添加重绘事件处理函数的定义:

void Widget::paintEvent(QPaintEvent *event)
{
    int x = startPoint.x();
    int y = startPoint.y();
    int width = endPoint.x() - x;
    int height = endPoint.y() - y;
    QPainter painter;
    painter.begin(&tempPix);
    painter.drawRect(x, y, width, height);
    painter.end();
    painter.begin(this);
    painter.drawPixmap(0, 0, tempPix);
    // 如果已经完成了绘制,那么更新缓冲区
    if(!isDrawing)
        pix = tempPix;
}

这里先在临时缓冲区tempPix中进行绘图,然后将其绘制到界面上。

最后判断是否已经完成了绘制,如果是,则将临时缓冲区中的内容复制到缓冲区中,这样就完成了整个矩形的绘制

这个例子中的关键是pix和 tempPix的相互复制,如果想将这个程序进行扩展,可以查看一下网站上的涂鸦板程序。

与这个例子很相似的一个应用是橡皮筋线,就是我们在Windows桌面上拖动鼠标出现的橡皮筋选择框。Qt中提供了QRubberBand类来实现橡皮筋线,使用它只需要在几个鼠标事件处理函数中进行设置即可,具体应用可以查看该类的帮助文档。

相关文章

微信公众号

最新文章

更多