QPainter双缓冲区实现一个简单画图软件

参考:QPainter双缓冲实现画直线

QT双缓冲实现画矩形

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;
}
相关推荐
nunumaymax2 小时前
【第三章-react 应用(基于 react 脚手架)】
前端·react.js·前端框架
郝学胜-神的一滴2 小时前
深入理解 epoll_wait:高性能 IO 多路复用核心解密
linux·服务器·开发语言·c++·网络协议
空中海2 小时前
第一章:Vue 基础与模板语法
前端·javascript·vue.js
小陈99cyh2 小时前
最新ubuntu22.04服务器上安装vmware虚拟机,附vmware的安装包
linux·运维·服务器·vmware
每天吃饭的羊2 小时前
水平,垂直居中
前端·javascript·html
广州灵眸科技有限公司2 小时前
瑞芯微(EASY EAI)RV1126B rknn-toolkit-lite2使用方法
linux·网络·人工智能·物联网·算法
张3232 小时前
Ansible实施任务控制
linux·ansible
白菜欣2 小时前
Linux权限
linux·运维·c++
卵男(章鱼)2 小时前
系统终端命令对比大全(Linux发行/macOS/Windows)
linux·运维·服务器·windows·macos