6.3 Qt5双缓冲机制

6.3 Qt5的双缓冲机制

  6.3.1 原理与设计

  所谓的双缓冲机制,既是在使用控件时,首先要将要绘制的内容绘制在一个图片中,再将图片一次性绘制到控件上。在早期的Qt中,由于是直接一次性绘制到控件上,没有经过先绘制到图片上再绘制到控件上,因此控件在重新绘制时会产生闪烁现象,当频繁绘制时这一现象更为明显。双缓冲机制(就是说先将要绘制的内容先绘制到图片上,再将这个图片绘制到控件上的这一过程)就能很好的消除这种闪烁现象。自Qt5版本之后,QWidget的控件已经可以自动处理闪烁的问题。在一些简单的场合中,我们无需再担心显示时会出现控件闪烁的问题,但这种双缓冲机制仍然在一些场合中也有着用武之地。尤其是在一些绘制内容非常复杂并需要频繁刷新时,或者是每次只需要刷新整个控件的一小部分时,仍然会使用到双缓冲机制。

  下面通过一个例子来演示双缓冲机制:

  实现一个简单的绘图工具,可以选择线型,线宽以及线的颜色。

  窗口的结构如下图所示:

  (1)新建Qt的GUI应用,之后项目名称为DrawWidget,基类选择QMainWindow。类名命名为MainWindow,不要选择"创建ui界面的复选框",建立该工程。

  (2)添加一个C++类,类名为DrawWidget,基类选择QWidget。

  (3)在drawwidget.h中添加如下成员

#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H

#include <QObject>
#include <QWidget>
#include <QMouseEvent>
#include <QPen>
#include <QPainter>
#include <QPaintEvent>
#include <QResizeEvent>
#include <QColor>
#include <QPixmap>
#include <QPoint>
#include <QColor>
#include <QPalette>
class DrawWidget : public QWidget
{
    Q_OBJECT
public:
    explicit DrawWidget(QWidget *parent = nullptr);

signals:

public slots:
public:
    void mouseMoveEvent(QMouseEvent *event) override;//重写鼠标移动事件
    void mousePressEvent(QMouseEvent *event) override;//鼠标按下事件
    void resizeEvent(QResizeEvent *event) override;//窗口的大小被重新绘制的事件
    void paintEvent(QPaintEvent *event) override;//绘图事件
public slots:
    void setStyle(int);
    void setWidth(int);
    void setColor(QColor);
    void clear();
private:
    QPixmap*m_pix;//要绘制的内容的图片的缓冲区Pixmap指针
    QPoint m_startPos;//绘制的起始坐标
    QPoint m_endPos;//绘制的终止坐标
    int m_style;
    int m_weight;
    QColor m_color;
};

#endif // DRAWWIDGET_H

  (4)在drawwidget.cpp中添加这些实现

#include "drawwidget.h"

DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent)
{
    this->setAutoFillBackground(true);
    this->setPalette(QPalette(Qt::white));
    m_pix = new QPixmap(size());//此pixmap对象用来随时准备接收绘制的内容
    m_pix->fill(Qt::white);//填充色为白色
    setMinimumSize(600,400);//设置绘制区域的最小尺寸
}

void DrawWidget::mouseMoveEvent(QMouseEvent *event)
{
    QPainter*painter = new QPainter;
    QPen pen;
    pen.setStyle((Qt::PenStyle)m_style);//设置画笔的线条风格
    pen.setColor(m_color);//设置画笔的颜色
    pen.setWidth(m_weight);//设置画笔的粗细

    painter->begin(m_pix);//我们直接将要绘制的内容绘制到图片的缓冲区上,不要去绘制到this指针上面
    painter->setPen(pen);//将画笔设置到画家上
    painter->drawLine(m_startPos,event->pos());//我们绘制一条直线,从鼠标按下时的点一直绘制到鼠标为拖动的过程中的位置
    m_startPos = event->pos();//绘制之后,那么新的开始的位置就应该是鼠标拖动了光标之后的位置
    painter->end();//结束绘制
    this->update();//重新绘制窗体
    /*
     *对于QPainter的构造函数中的绘图设备QPainterDevice,实际上这个就相当于我想在哪个绘图设备上进行
     * 绘制图形,理论上来说,对于Qt的显示控件(基于QWidget的一切类都可以作为绘图设备),当然也包括我们之前
     * 打印机对象也是可以的。这些在IO上具有图像的输出的这些抽象的绘图设备都可以作为QPainter的绘图设备
    */
}

void DrawWidget::mousePressEvent(QMouseEvent *event)
{
    m_startPos = event->pos();//当鼠标被按下时,记录下按下鼠标时鼠标光标的位置坐标
}

void DrawWidget::resizeEvent(QResizeEvent *event)
{
    //由于当窗口的大小发生了改变,但我们的PixMap的大小和原先是一样的,因此需要重新new出pixmap对象,再重新将原先的Pix绘制上去
    if(height()>m_pix->height()||width()>m_pix->width())
    {
        QPixmap*newPix = new QPixmap(size());
        newPix = new QPixmap(size());
        newPix->fill(Qt::white);
        QPainter painter(newPix);
        painter.drawPixmap(QPoint(0,0),*newPix);
        painter.end();
    }
    QWidget::resizeEvent(event);
}

void DrawWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawPixmap(QPoint(0,0),*m_pix);
}

void DrawWidget::setStyle(int style)
{
    m_style = style;
}

void DrawWidget::setWidth(int width)
{
    m_weight = width;
}

void DrawWidget::setColor(QColor color)
{
    m_color = color;
}

void DrawWidget::clear()
{
    //清理绘图区的内容,我们只需要重新new一个干净的,没有任何内容且只有白色背景的新的Pixmap即可
    QPixmap*newPix = new QPixmap(size());
    newPix->fill(Qt::white);
    m_pix = newPix;
    update();

}

  (5)在mainwindow.h中添加这些控件的成员指针

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>
#include <QColorDialog>
#include <QComboBox>
#include <QSpinBox>
#include <QToolBar>
#include <QAction>
#include "drawwidget.h"
#include <QToolButton>
#include <QPushButton>
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private slots:
    void lineStyle(const QString &text);
    void lineWidth(int i);
    void lineColor();
    void clear();
private:
    QToolBar*m_toolBar;
private:
    QLabel*m_lineStyleLabel;
    QComboBox*m_lineStyleComboBox;
    QLabel*m_lineWidthLabel;
    QSpinBox*m_lineWidthSpinBox;
    QToolButton*m_colorToolButton;
    QPushButton*m_clearPushButton;
    DrawWidget*m_drawWidget;
};
#endif // MAINWINDOW_H

  (6)在mainwindow.cpp中添加这些实现

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent):QMainWindow(parent)
{
    m_toolBar = new QToolBar(QString("绘图工具栏"));

    m_lineStyleLabel = new QLabel(QString("线型风格:"));
    m_lineStyleComboBox = new QComboBox;
    m_lineStyleComboBox->addItem(QString("Qt::SolidLine"));
    m_lineStyleComboBox->addItem(QString("Qt::DashLine"));
    m_lineStyleComboBox->addItem(QString("Qt::DotLine"));
    m_lineStyleComboBox->addItem(QString("Qt::DashDotLine"));
    m_lineStyleComboBox->addItem(QString("Qt::DashDotDotLine"));
    m_lineStyleComboBox->addItem(QString("Qt::CustomDashLine"));

    m_lineWidthLabel = new QLabel(QString("线宽:"));
    m_lineWidthSpinBox = new QSpinBox;
    m_lineWidthSpinBox->setRange(1,20);

    m_colorToolButton = new QToolButton;
    m_colorToolButton->setToolTip(QString("颜色"));
    m_colorToolButton->setAutoFillBackground(true);
    m_colorToolButton->setPalette(QPalette(Qt::red));

    m_clearPushButton = new QPushButton(QString("清除"));

    m_toolBar->addWidget(m_lineStyleLabel);
    m_toolBar->addWidget(m_lineStyleComboBox);
    m_toolBar->addWidget(m_lineWidthLabel);
    m_toolBar->addWidget(m_lineWidthSpinBox);
    m_toolBar->addWidget(m_colorToolButton);
    m_toolBar->addWidget(m_clearPushButton);

    this->addToolBar(m_toolBar);
    m_drawWidget = new DrawWidget;
    this->setCentralWidget(m_drawWidget);

    connect(this->m_lineStyleComboBox,SIGNAL(activated(const QString &)),this,SLOT(lineStyle(const QString &)));
    connect(this->m_lineWidthSpinBox,SIGNAL(valueChanged(int)),this,SLOT(lineWidth(int)));
    connect(this->m_colorToolButton,&QToolButton::clicked,this,&MainWindow::lineColor);
    connect(this->m_clearPushButton,&QPushButton::clicked,this,&MainWindow::clear);
}

MainWindow::~MainWindow()
{
}

void MainWindow::lineStyle(const QString &text)
{
    if("Qt::SolidLine" == text)
    {
        m_drawWidget->setStyle(Qt::SolidLine);
    }
    else if("Qt::DashLine" == text)
    {
        m_drawWidget->setStyle(Qt::DashLine);
    }
    else if("Qt::DotLine" == text)
    {
        m_drawWidget->setStyle(Qt::DotLine);
    }
    else if("Qt::DashDotLine" == text)
    {
        m_drawWidget->setStyle(Qt::DashDotLine);
    }
    else if("Qt::DashDotDotLine" == text)
    {
        m_drawWidget->setStyle(Qt::DashDotDotLine);
    }
    else if("Qt::CustomDashLine" == text)
    {
        m_drawWidget->setStyle(Qt::CustomDashLine);
    }
}

void MainWindow::lineWidth(int i)
{
    m_drawWidget->setWidth(i);
}

void MainWindow::lineColor()
{
    QColor color = QColorDialog::getColor();
    m_colorToolButton->setPalette(color);
    m_drawWidget->setColor(color);
}

void MainWindow::clear()
{
    m_drawWidget->clear();
}

  (6)运行效果图

  本章节代码:https://files.cnblogs.com/files/blogs/792763/DrawWidget.zip?t=1718474107&download=true

posted @ 2024-06-16 01:55  蜡笔小新Pointer  阅读(87)  评论(0)    收藏  举报