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

浙公网安备 33010602011771号