二十四、双缓冲机制
所谓双缓冲机制,是指在绘制控件时,首先将要绘制的内容绘制在一个图片中,再将图片一次性的绘制到控件上。
实现以下实例
接下来我们将一一介绍实现的功能
首先我们创建一个QMainWindow工程
继续创建一个c++Class文件,继承QWidget,取名为“drawPro”,后面我们的主要功能在这个类进行实现。
首先看drawpro.h
#ifndef DRAWPRO_H
#define DRAWPRO_H#include <QWidget>#include<QtGui>
#include<QMouseEvent>
#include<QPaintEvent>
#include<QResizeEvent>
#include<QColor>
#include<QPen>
#include<QPixmap>
#include<QPainter>
#include<QPalette>class drawPro : public QWidget
{Q_OBJECT
public:explicit drawPro(QWidget *parent = nullptr);void mousePressEvent(QMouseEvent *); //鼠标点击事件void mouseMoveEvent(QMouseEvent *); //鼠标移动事件void paintEvent(QPaintEvent *); //绘图事件void resizeEvent(QResizeEvent *);signals:private:QPixmap *pix;QPoint startPos;QPoint endPos;int style,widths;QColor color;public slots:void setStyle(int); //设置风格void setWidth(int); //设置线宽度void setColor(QColor); //设置线颜色void clearFunc(); //清除函数};#endif // DRAWPRO_H
我们需要重写鼠标点击事件、鼠标移动事件、绘制事件、大小调整事件。并且也要实现设置风格、线的宽度、线的颜色和清除函数。
drawpro.cpp
实现构造函数
drawPro::drawPro(QWidget *parent) : QWidget(parent)
{setAutoFillBackground(true);setPalette(QPalette(Qt::white));pix=new QPixmap(size());pix->fill(Qt::white);setMinimumSize(600,400);
}
首先把自动填充背景打开,设置背景颜色为白色,然后创建QPixmap对象,并设置大小为窗口的大小,填充颜色为白色。
QPixmap::QPixmap(const QSize &size):
这是一个重载函数。
构造给定大小的像素图。
警告:这将创建一个带有未初始化数据的QPixmap。在使用QPainter绘图之前,调用fill()来用适当的颜色填充像素图。
void QPixmap::fill(const QPaintDevice *device, const QPoint &p):
这个功能已经过时了。提供它是为了保持旧源代码的工作。我们强烈建议不要在新代码中使用它。
使用QPainter或填充(QColor)过载代替。
void setMinimumSize(int minw, int minh):
此属性保存小部件的最小大小
不能将小部件调整为小于最小小部件大小的大小。如果当前大小较小,则小部件的大小将强制为最小大小。
这个函数设置的最小大小将覆盖QLayout定义的最小大小。为了取消最小大小的设置,使用QSize(0,0)的值。
默认情况下,此属性包含宽度和高度为零的大小。
实现set系列函数
void drawPro::setStyle(int s)
{style=s;
}void drawPro::setWidth(int w)
{widths=w;
}void drawPro::setColor(QColor c)
{color=c;
}
实现清除函数
void drawPro::clearFunc() //创建一个新的pixmap把原来的pixmap覆盖掉,就形成了清除功能
{QPixmap *clearPixmap=new QPixmap(size());clearPixmap->fill(Qt::white);pix=clearPixmap;update();
}
创建一个新的QPixmap对象覆盖旧的QPixmap对象。进而实现了清除功能。但是要记得更新
实现鼠标移动事件
void drawPro::mouseMoveEvent(QMouseEvent *m) //鼠标移动事件
{QPainter *painter=new QPainter;QPen pen;pen.setStyle((Qt::PenStyle)style);pen.setWidth(widths);pen.setColor(color);painter->begin(pix);painter->setPen(pen);painter->drawLine(startPos,m->pos());painter->end();startPos=m->pos();update();
}
初始化笔的一些值,然后把笔放在绘制对象里,让笔跟着绘制对象的绘制直线函数进行绘图。
bool QPainter::begin(QPaintDevice *device) :
开始绘制绘制设备,如果成功返回true;否则返回false。
注意,当调用begin()时,所有的绘制设置(setPen(), setBrush()等)都会重置为默认值。
void QPainter::drawLine(const QPoint &p1, const QPoint &p2):
这是一个重载函数。
从p1到p2画一条线。
QPoint QMouseEvent::pos() const:
返回鼠标光标相对于接收事件的小部件的位置。
如果由于鼠标事件而移动小部件,请使用globalPos()返回的全局位置来避免抖动运动。
bool QPainter::end():
绘画结束。绘制时使用的任何资源都会被释放。通常不需要调用this,因为它是由析构函数调用的。
如果画工不再活动则返回true;否则返回false。
实现鼠标点击事件
void drawPro::mousePressEvent(QMouseEvent *m)
{startPos=m->pos();
}
实现绘制事件
void drawPro::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.drawPixmap(QPoint(0,0),*pix);
}
用于在窗口中绘制一个图片。具体来说,它使用了QPainter类来创建一个画家对象,然后使用drawPixmap()函数在窗口中绘制一个图片。其中,QPoint(0,0)表示图片在窗口中的位置,*pix表示要绘制的图片对象。需要注意的是,这段代码没有对图片进行缩放或裁剪,因此图片会按照原始大小显示在窗口中。
void QPainter::drawPixmap(const QPoint &point, const QPixmap &pixmap):
这是一个重载函数。
在给定点的原点上绘制给定的像素图
实现大小调整事件
void drawPro::resizeEvent(QResizeEvent *r)
{if(height() > pix->height() || width() > pix->width()){QPixmap *newPix=new QPixmap(size());newPix->fill(Qt::white);QPainter ps(newPix);ps.drawPixmap(QPoint(0,0),*pix);pix=newPix;}QWidget::resizeEvent(r);
}
当窗口大小改变时,会触发resizeEvent()函数。在函数中,首先判断当前窗口的高度和宽度是否大于原始图片的高度和宽度,如果是,则创建一个新的QPixmap对象newPix,并将其填充为白色。接着,创建一个QPainter对象ps,将原始图片pix绘制在newPix上,并将pix指向newPix。最后,调用QWidget的resizeEvent()函数。
这段代码的作用是在窗口大小改变时,根据窗口大小动态调整图片的大小,并将原始图片绘制在新的图片上。如果窗口变大,则新的图片会被填充为白色,并将原始图片绘制在新的图片上;如果窗口变小,则原始图片会被裁剪。
void QWidget::resizeEvent(QResizeEvent *event):
此事件处理程序可以在子类中重新实现,以接收在事件参数中传递的小部件调整大小事件。当resizeEvent()被调用时,小部件已经有了新的几何形状。旧的大小可以通过QResizeEvent::oldSize()来访问。
处理完调整大小事件后,小部件将被擦除并立即接收一个绘制事件。在此处理程序中不需要(或不应该)进行绘图。
完整代码
#include "drawpro.h"drawPro::drawPro(QWidget *parent) : QWidget(parent)
{setAutoFillBackground(true);setPalette(QPalette(Qt::white));pix=new QPixmap(size());pix->fill(Qt::white);setMinimumSize(600,400);
}void drawPro::setStyle(int s)
{style=s;
}void drawPro::setWidth(int w)
{widths=w;
}void drawPro::setColor(QColor c)
{color=c;
}void drawPro::clearFunc() //创建一个新的pixmap把原来的pixmap覆盖掉,就形成了清除功能
{QPixmap *clearPixmap=new QPixmap(size());clearPixmap->fill(Qt::white);pix=clearPixmap;update();
}void drawPro::mouseMoveEvent(QMouseEvent *m) //鼠标移动事件
{QPainter *painter=new QPainter;QPen pen;pen.setStyle((Qt::PenStyle)style);pen.setWidth(widths);pen.setColor(color);painter->begin(pix);painter->setPen(pen);painter->drawLine(startPos,m->pos());painter->end();startPos=m->pos();update();
}void drawPro::mousePressEvent(QMouseEvent *m)
{startPos=m->pos();
}void drawPro::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.drawPixmap(QPoint(0,0),*pix);
}void drawPro::resizeEvent(QResizeEvent *r)
{if(height() > pix->height() || width() > pix->width()){QPixmap *newPix=new QPixmap(size());newPix->fill(Qt::white);QPainter ps(newPix);ps.drawPixmap(QPoint(0,0),*pix);pix=newPix;}QWidget::resizeEvent(r);
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>#include"drawpro.h"#include<QLabel>
#include<QToolButton>
#include<QToolBar>
#include<QComboBox>
#include<QSpinBox>
#include<QColorDialog>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();void createToolBarFunc();private:Ui::MainWindow *ui;drawPro *draw;QLabel *labelStyle;QComboBox *comboboxLabelStyle;QLabel *labelWidth;QSpinBox *spinboxLabelWidth;QToolButton *colorButton;QToolButton *clearButton;private slots:void dispStyle();void dispColor();
};
#endif // MAINWINDOW_H
mainwindow.cpp
首先实现构造函数
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);draw=new drawPro;setCentralWidget(draw);createToolBarFunc();setMinimumSize(600,400);dispStyle();draw->setWidth(spinboxLabelWidth->value());draw->setColor(Qt::black);
}
void QMainWindow::setCentralWidget(QWidget *widget):
将给定的小部件设置为主窗口的中心小部件。
注意:QMainWindow获取小部件指针的所有权,并在适当的时候删除它。
void MainWindow::createToolBarFunc()
{QToolBar *toolBar=addToolBar("Tool");labelStyle=new QLabel("线型风格:");comboboxLabelStyle=new QComboBox;comboboxLabelStyle->addItem("实线",static_cast<int>(Qt::SolidLine));comboboxLabelStyle->addItem("冲线",static_cast<int>(Qt::DashLine));comboboxLabelStyle->addItem("点点线",static_cast<int>(Qt::DashDotDotLine));comboboxLabelStyle->addItem("虚线",static_cast<int>(Qt::DotLine));connect(comboboxLabelStyle,SIGNAL(activated(int)),this,SLOT(dispStyle()));labelWidth=new QLabel("线型宽度:");spinboxLabelWidth=new QSpinBox();connect(spinboxLabelWidth,SIGNAL(valueChanged(int)),draw,SLOT(setWidth(int)));colorButton=new QToolButton;QPixmap pixmap(20,20);pixmap.fill(Qt::black);colorButton->setIcon(QIcon(pixmap));connect(colorButton,&QToolButton::clicked,this,&MainWindow::dispColor);clearButton=new QToolButton;clearButton->setText("清除");connect(clearButton,&QToolButton::clicked,draw,&drawPro::clearFunc);toolBar->addWidget(labelStyle);toolBar->addWidget(comboboxLabelStyle);toolBar->addWidget(labelWidth);toolBar->addWidget(spinboxLabelWidth);toolBar->addWidget(colorButton);toolBar->addWidget(clearButton);}
QToolBar *QMainWindow::addToolBar(const QString &title):
这是一个重载函数。
创建QToolBar对象,将其窗口标题设置为title,并将其插入到顶部工具栏区域。
实现槽函数
void MainWindow::dispStyle()
{draw->setStyle(comboboxLabelStyle->itemData(comboboxLabelStyle->currentIndex(),Qt::UserRole).toInt());
}void MainWindow::dispColor()
{QColor color=QColorDialog::getColor(static_cast<int>(Qt::black),this);if(color.isValid()){draw->setColor(color);QPixmap ps(20,20);ps.fill(color);}
}
完整代码
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);draw=new drawPro;setCentralWidget(draw);createToolBarFunc();setMinimumSize(600,400);dispStyle();draw->setWidth(spinboxLabelWidth->value());draw->setColor(Qt::black);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::createToolBarFunc()
{QToolBar *toolBar=addToolBar("Tool");labelStyle=new QLabel("线型风格:");comboboxLabelStyle=new QComboBox;comboboxLabelStyle->addItem("实线",static_cast<int>(Qt::SolidLine));comboboxLabelStyle->addItem("冲线",static_cast<int>(Qt::DashLine));comboboxLabelStyle->addItem("点点线",static_cast<int>(Qt::DashDotDotLine));comboboxLabelStyle->addItem("虚线",static_cast<int>(Qt::DotLine));connect(comboboxLabelStyle,SIGNAL(activated(int)),this,SLOT(dispStyle()));labelWidth=new QLabel("线型宽度:");spinboxLabelWidth=new QSpinBox();connect(spinboxLabelWidth,SIGNAL(valueChanged(int)),draw,SLOT(setWidth(int)));colorButton=new QToolButton;QPixmap pixmap(20,20);pixmap.fill(Qt::black);colorButton->setIcon(QIcon(pixmap));connect(colorButton,&QToolButton::clicked,this,&MainWindow::dispColor);clearButton=new QToolButton;clearButton->setText("清除");connect(clearButton,&QToolButton::clicked,draw,&drawPro::clearFunc);toolBar->addWidget(labelStyle);toolBar->addWidget(comboboxLabelStyle);toolBar->addWidget(labelWidth);toolBar->addWidget(spinboxLabelWidth);toolBar->addWidget(colorButton);toolBar->addWidget(clearButton);}void MainWindow::dispStyle()
{draw->setStyle(comboboxLabelStyle->itemData(comboboxLabelStyle->currentIndex(),Qt::UserRole).toInt());
}void MainWindow::dispColor()
{QColor color=QColorDialog::getColor(static_cast<int>(Qt::black),this);if(color.isValid()){draw->setColor(color);QPixmap ps(20,20);ps.fill(color);}
}
本次项目的难度在于实现上面几个事件函数,需要注意一些细节,比如在改变笔的颜色的时候,我们应该先保存当前绘制的图片。