https://developer.aliyun.com/article/1507912
Qt 双缓冲机制
Qt双缓冲机制是一种优化技术,用于减少图形闪烁和提高图形性能,特别是在需要频繁重绘的情况下。
简单来说,双缓冲机制就是在一个缓冲区中进行图形绘制,然后将绘制好的图形复制到屏幕中显示,这样能够有效地减少图形闪烁的问题,并且提高了绘图的速度和效率。
DrawWidget通过QPainter进行绘制直线
drawwidget.h
#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H
#include <QObject>
#include <QWidget>
#include <QPixmap>
#include <QMouseEvent>
#include <QPainter>
#include <QPen>
class DrawWidget : public QWidget
{
Q_OBJECT
public:
explicit DrawWidget(QWidget *parent = nullptr);
~DrawWidget();
void setStyle(int s)
{
style = s;
}
void setWidth(int w)
{
width = w;
}
void setColor(QColor c)
{
color = c;
}
void clear();
protected:
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void paintEvent(QPaintEvent *) override;
void resizeEvent(QResizeEvent *event) override;
signals:
private:
QPixmap pix;
QPoint startPos;
int style; //画笔线型风格
int width; //画笔线宽
QColor color; //画笔颜色
};
#endif // DRAWWIDGET_H
#include "drawwidget.h"
#include <QDebug>
DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent)
{
setAutoFillBackground(true); //对窗体背景色的设置
setPalette(QPalette(Qt::white));
pix = QPixmap(size()); //此QPixmap对象用于准备随时接收绘制的内容
pix.fill(Qt::white); //填充背景色为白色
setMinimumSize(600, 400); //设置绘制区窗体的最小尺寸
}
DrawWidget::~DrawWidget()
{
qDebug() << "~DrawWidget called";
}
void DrawWidget::clear()
{
pix = QPixmap(size());
pix.fill(Qt::white);
update();
}
void DrawWidget::mousePressEvent(QMouseEvent *e)
{
startPos = e->pos();
}
void DrawWidget::mouseMoveEvent(QMouseEvent *e)
{
QPainter painter(&pix); // 在pix上绘制
QPen pen; //新建一个QPen对象
pen.setStyle((Qt::PenStyle)style); //
pen.setWidth(width); //设置画笔的线宽值
pen.setColor(color); //设置画笔的颜色
painter.setPen(pen); //将QPen对象应用到绘制对象中
//绘制从startPos到鼠标当前位置的直线
painter.drawLine(startPos, e->pos());
startPos = e->pos(); //更新鼠标的当前位置,为下次绘制做准备
update(); //重绘绘制区窗体
}
void DrawWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawPixmap(QPoint(0, 0), pix);
}
void DrawWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
// 当窗口大小改变时,重新创建 pixmap
pix = QPixmap(size());
pix.fill(Qt::white);
}
drawrect用来绘制矩形,使用双缓冲实现
#ifndef DRAWRECT_H
#define DRAWRECT_H
#include <QObject>
#include <QWidget>
#include <QPixmap>
#include <QMouseEvent>
#include <QPainter>
class DrawRect : public QWidget
{
Q_OBJECT
public:
explicit DrawRect(QWidget *parent = nullptr);
protected:
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void paintEvent(QPaintEvent *) override;
void resizeEvent(QResizeEvent *event) override;
signals:
private:
//临时画布 tempPix用来作为临时缓冲区,当鼠标正在拖动矩形进行绘制时,将内容先绘制到tempPix上,然后将tempPix绘制到界面上
QPixmap tmpPix;
// pix作为缓冲区,用来保存已经完成的绘制。当松开鼠标完成矩形的绘制后,则将tempPix的内容复制到pix上
QPixmap pix;
/*
为了绘制时不显示拖影,而且保证以前绘制的内容不消失,那么在移动鼠标过程中,每绘制一次,
都要在绘制这个矩形的原来的图像上进行绘制,所以需要在每次绘制tempPix之前,
先将pix的内容复制到tempPix上。因为这里有两个QPixmap对象,也可以说有两个缓冲区,所以称之为双缓冲绘图
*/
//标志是否正在绘图
bool isDrawing;
QPoint lastPoint;
QPoint endPoint;
};
#endif // DRAWRECT_H
#include "drawrect.h"
DrawRect::DrawRect(QWidget *parent) : QWidget(parent)
{
isDrawing = false;
lastPoint = QPoint(); // 初始化为无效点
endPoint = QPoint(); // 初始化为无效点
setAutoFillBackground(true); //对窗体背景色的设置
setPalette(QPalette(Qt::white));
// 在构造函数中,size() 可能是 (0,0),稍后在 showEvent 中重新创建
pix = QPixmap(size());
if (pix.isNull() || pix.width() == 0 || pix.height() == 0) {
pix = QPixmap(600, 400); // 提供默认大小
}
pix = QPixmap(size()); //此QPixmap对象用于准备随时接收绘制的内容
pix.fill(Qt::white); //填充背景色为白色
tmpPix = QPixmap(size()); //此QPixmap对象用于准备随时接收绘制的内容
tmpPix.fill(Qt::white);
setMinimumSize(600, 400); //设置绘制区窗体的最小尺寸
}
void DrawRect::mousePressEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton)
{
lastPoint = e->pos();
//正在绘图
isDrawing = true;
}
endPoint = lastPoint;
}
void DrawRect::mouseReleaseEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton)
{
endPoint = e->pos();
//结束绘图
isDrawing = false;
update();
}
}
void DrawRect::paintEvent(QPaintEvent *ev)
{
int x, y, w, h;
x = qMin(lastPoint.x(), endPoint.x());
y = qMin(lastPoint.y(), endPoint.y());
w = qAbs(endPoint.x() - lastPoint.x());
h = qAbs(endPoint.y() - lastPoint.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);
}
}
void DrawRect::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
// 当窗口大小改变时,重新创建 pixmap 并保持原有内容
QPixmap newPix = QPixmap(size());
newPix.fill(Qt::white);
// 将旧内容复制到新 pixmap
QPainter p(&newPix);
p.drawPixmap(0, 0, pix);
pix = newPix;
tmpPix = QPixmap(size());
tmpPix.fill(Qt::white);
}
mainwindow中调用
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "drawwidget.h"
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
DrawWidget* drawWidget;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
// drawWidget = new DrawWidget(this); //新建一个DrawWidget对象
// setCentralWidget(drawWidget); //新建的DrawWidget对象作为主窗口的中央窗体
//// createToolBar(); //实现一个工具栏
// setMinimumSize(600, 400); //设置主窗口的最小尺寸
//// showStyle(); //初始化线型,设置控件中的当前值作为初始值
// drawWidget->setStyle(Qt::SolidLine);
// drawWidget->setWidth(2); //初始化线宽
// drawWidget->setColor(Qt::black); //初始化颜色
drawRect = new DrawRect();
setCentralWidget(drawRect); //新建的DrawWidget对象作为主窗口的中央窗体
setMinimumSize(600, 400); //设置主窗口的最小尺寸
}
MainWindow::~MainWindow()
{
delete ui;
}