所谓双缓冲机制,是指在绘制控件时,首先将要绘制的内容绘制在一个图片中,再将图片一次性的绘制到控件上。
实现以下实例
接下来我们将一一介绍实现的功能
首先我们创建一个QMainWindow工程
继续创建一个c++Class文件,继承QWidget,取名为“drawPro”,后面我们的主要功能在这个类进行实现。
首先看drawpro.h
#ifndef DRAWPRO_H
#define DRAWPRO_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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。在使用QPainter绘图之前,调用fill()来用适当的颜色填充像素图。
这个功能已经过时了。提供它是为了保持旧源代码的工作。我们强烈建议不要在新代码中使用它。
使用QPainter或填充(QColor)过载代替。
此属性保存小部件的最小大小
不能将小部件调整为小于最小小部件大小的大小。如果当前大小较小,则小部件的大小将强制为最小大小。
这个函数设置的最小大小将覆盖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();
}
初始化笔的一些值,然后把笔放在绘制对象里,让笔跟着绘制对象的绘制直线函数进行绘图。
开始绘制绘制设备,如果成功返回true;否则返回false。
注意,当调用begin()时,所有的绘制设置(setPen(), setBrush()等)都会重置为默认值。
这是一个重载函数。
从p1到p2画一条线。
返回鼠标光标相对于接收事件的小部件的位置。
如果由于鼠标事件而移动小部件,请使用globalPos()返回的全局位置来避免抖动运动。
绘画结束。绘制时使用的任何资源都会被释放。通常不需要调用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 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()函数。
这段代码的作用是在窗口大小改变时,根据窗口大小动态调整图片的大小,并将原始图片绘制在新的图片上。如果窗口变大,则新的图片会被填充为白色,并将原始图片绘制在新的图片上;如果窗口变小,则原始图片会被裁剪。
此事件处理程序可以在子类中重新实现,以接收在事件参数中传递的小部件调整大小事件。当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
#include"drawpro.h"
#include
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
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);
}
将给定的小部件设置为主窗口的中心小部件。
注意:QMainWindow获取小部件指针的所有权,并在适当的时候删除它。
void MainWindow::createToolBarFunc()
{
QToolBar *toolBar=addToolBar("Tool");
labelStyle=new QLabel("线型风格:");
comboboxLabelStyle=new QComboBox;
comboboxLabelStyle->addItem("实线",static_cast(Qt::SolidLine));
comboboxLabelStyle->addItem("冲线",static_cast(Qt::DashLine));
comboboxLabelStyle->addItem("点点线",static_cast(Qt::DashDotDotLine));
comboboxLabelStyle->addItem("虚线",static_cast(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对象,将其窗口标题设置为title,并将其插入到顶部工具栏区域。
实现槽函数
void MainWindow::dispStyle()
{
draw->setStyle(comboboxLabelStyle->itemData(comboboxLabelStyle->currentIndex(),Qt::UserRole).toInt());
}
void MainWindow::dispColor()
{
QColor color=QColorDialog::getColor(static_cast(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(Qt::SolidLine));
comboboxLabelStyle->addItem("冲线",static_cast(Qt::DashLine));
comboboxLabelStyle->addItem("点点线",static_cast(Qt::DashDotDotLine));
comboboxLabelStyle->addItem("虚线",static_cast(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(Qt::black),this);
if(color.isValid())
{
draw->setColor(color);
QPixmap ps(20,20);
ps.fill(color);
}
}
本次项目的难度在于实现上面几个事件函数,需要注意一些细节,比如在改变笔的颜色的时候,我们应该先保存当前绘制的图片。