二十四、双缓冲机制

二十四、双缓冲机制

所谓双缓冲机制,是指在绘制控件时,首先将要绘制的内容绘制在一个图片中,再将图片一次性的绘制到控件上。

实现以下实例

接下来我们将一一介绍实现的功能

首先我们创建一个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_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);
}

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);
    }
}

本次项目的难度在于实现上面几个事件函数,需要注意一些细节,比如在改变笔的颜色的时候,我们应该先保存当前绘制的图片。

  • 23
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mo Yan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值