• Qt5开发从入门到精通——第六篇三节( 图像与图片——双缓冲机制)


    欢迎小伙伴的点评✨✨,相互学习、互关必回、全天在线🍳🍳🍳
    博主🧑🧑 本着开源的精神交流Qt开发的经验、将持续更新续章,为社区贡献博主自身的开源精神👩‍🚀


    前言

    本章首先介绍几种主要位置函数及其之间的区别,以及各种与位置相关函数的使用场合;然后,通过一个简单绘图工具实例,介绍利用 QPainter 和 QPainterPath 两种方法绘制各种基础图形。


    一、双缓冲机制概述

    所谓双缓冲机制,是指在绘制控件时,首先将要绘制的内容绘制在一个图片中,再将图片一次性地绘制到控件上。在早期的 Qt 版本中,若直接在控件上进行绘制工作,则在控件重绘时会产生闪烁的现象,控件重绘频繁时,闪烁尤为明显。双缓冲机制可以有效地消除这种闪烁现象。自Qt 5 版本之后, QWidget 控件已经能够自动处理闪烁的问题。因此,在控件上直接绘图时,不用再操心显示的闪烁问题,但双缓冲机制在很多场合仍然有其用武之地。当所需绘制的内容较复杂并需要频繁刷新,或者每次只需要刷新整个控件的一小部分时,仍应尽量采用双缓冲机制。

    二、效果实例

    图一
    在这里插入图片描述
    图二
    在这里插入图片描述


    三、原码实例

    drawwidget.h

    #ifndef DRAWWIDGET_H
    #define DRAWWIDGET_H
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    class DrawWidget : public QWidget
    {
        Q_OBJECT
    public:
        explicit DrawWidget(QWidget *parent = nullptr);
        void mousePressEvent(QMouseEvent *);
        void mouseMoveEvent(QMouseEvent *);
        void paintEvent(QPaintEvent *);
        void resizeEvent(QResizeEvent *);
    signals:
    
    public slots:
        void setStyle(int);
        void setWidth(int);
        void setColor(QColor);
        void clear();
    private:
        QPixmap *pix;
        QPoint startPos;
        QPoint endPos;
        int style;
        int weight;
        QColor color;
    };
    
    #endif // DRAWWIDGET_H
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "drawwidget.h"
    #include 
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = 0);
        ~MainWindow();
        void createToolBar();
    public slots:
    void ShowStyle();
    void ShowColor();
    
    private:
    
    DrawWidget *drawWidget;
    QLabel *styleLabel;
    QComboBox *styleComboBox;
    QLabel *widthLabel;
    QSpinBox *widthSpinBox;
    QToolButton *colorBtn;
    QToolButton *clearBtn;
    };
    
    #endif // MAINWINDOW_H
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    main.cpp

    #include "mainwindow.h"
    #include 
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QFont font("ZYSongl8030",12);    //设置字体
        a.setFont(font);
        MainWindow w;
        w.show();
    
        return a.exec();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    drawwidget.cpp

    #include "drawwidget.h"
    
    DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent)
    {
        setAutoFillBackground (true);      //对窗体背景色的设置
        setPalette(QPalette(Qt::white));
        pix =new QPixmap (size());         //对此QPixmap对象用于准备随时接收绘制的内容
        pix->fill(Qt::white);             //填充背景色为白色
        setMinimumSize(600,400);          //设置绘制区窗体的最小尺寸
    
    }
    void DrawWidget::setStyle(int s)     //setStyle() 函数接收主窗口传来的线型风格参数
    {
        style = s;
    
    }
    void DrawWidget::setWidth(int w)    //setWidth() 函数接收主窗口传来的线宽参数值
    {
        weight = w;
    }
    void DrawWidget::setColor(QColor c)  //setColor() 函数接收主窗口传来的画笔颜色值
    {
        color = c;
    
    }
    void DrawWidget::mousePressEvent(QMouseEvent *e) //重定义鼠标按下事件 mousePressEvent() , 在按下鼠标按键时,记录当前的鼠标位置值startPos 。
    {
        startPos = e->pos();
    }
    
    void DrawWidget::mouseMoveEvent(QMouseEvent *e)
    {
        QPainter *painter= new QPainter;   //新建一个 QPainter 对象
        QPen pen;                          //新建一个 QPen 对象
        pen.setStyle((Qt::PenStyle)style);//设置画笔的线型, style 表示当前选择的线型是Qt::PenStyle 枚举数据中的第几个元素。
        pen.setWidth(weight);                 //设置画笔的线宽值
        pen.setColor(color);                  //设置画笔的颜色
        painter->begin(pix);                  
        /*以 QPixmap 对象为 QPaintDevice 参数绘制。在构
    造一个 QPainter 对象时,就立即开始对绘画设备进行绘制。此构造 QPainter 对象是短时期的,
    如应定义在 QWidget: :paintEvent()中,并只能调用一次。此构造函数调用开始千 begin()函数,并
    且在 QPainter 的析构函数中自动调用 end()函数。由于当一个 QPainter 对象的初始化失败时构造函
    数不能提供反馈信息,所以在绘制外部设备时应使用 begin()和 end()函数,如打印机等外部设备。*/
        painter->setPen(pen);                 //将 QPen 对象应用到绘制对象中
        //绘制从 startPos 到鼠标当前位置的直线
        painter->drawLine (startPos, e->pos ());
        painter->end();
        startPos=e->pos();                   //更新鼠标的当前位置,为下次绘制做准备
        update();                            //重绘绘制区窗体
    
    
    }
    void DrawWidget::paintEvent(QPaintEvent *)  //重绘函数 paintEventO完成绘制区窗体的更新工作,只需调用 drawPixmap()函数将用千接收
                                               //图形绘制的 QPixmap 对象绘制在绘制区窗体控件上即可。
    {
        QPainter painter(this);
        painter.drawPixmap(QPoint(0,0),*pix);
    }
    void DrawWidget::resizeEvent(QResizeEvent *event)
    {
        if(height()>pix->height()||width()>pix->width())  //判断改变后的窗体长或宽是否大于原窗体的长和宽 。 
                                                          //若大于则进行相应的调整,否则直接调用 QWidget 的 resizeEvent()函数返回。
        {
            QPixmap *newPix = new QPixmap (size());   //创建一个新的 QPixmap 对象
            newPix->fill(Qt::white);     //填充新 QPixmap 对象 newPix 的颜色为白色背景色
            QPainter p(newPix);
            p.drawPixmap(QPoint(0,0),*pix);  //在 newPix 中绘制原 pix 中的内容
            pix = newPix;                    //将 newPix 赋值给 pix 作为新的绘制图形接收对象
    
        }
        QWidget::resizeEvent(event);        ///完成其余的工作
    }
    void DrawWidget::clear()      //clear() 函数完成绘制区的清除工作,只需调用一个新的、
                                 //干净的 QPixmap 对象来代替 pix,并调用 update()函数重绘即可
    {
        QPixmap *clearPix =new QPixmap (size());
        clearPix->fill (Qt::white);
        pix= clearPix;
        update();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    mainwindow.cpp

    #include "mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        drawWidget =new DrawWidget; //新建一个 DrawWidget 对象
        setCentralWidget(drawWidget); //新建的 DrawWidget 对象作为主窗口的中央窗体
        createToolBar();             //实现一个工具栏
        setMinimumSize(600,400);     //设置主窗口的最小尺寸
        ShowStyle();                 //初始化线型,设置控件中的当前值作为初始值
        drawWidget->setWidth(widthSpinBox->value()); //初始化线宽
        drawWidget->setColor (Qt::black);            // 初始化颜色
    
    
    }
    
    MainWindow::~MainWindow()
    {
    
    }
    
    void MainWindow::createToolBar()
    {
        QToolBar *toolBar = addToolBar("Tool");     //为主窗口新建一个工具栏对象
        styleLabel =new QLabel(tr(" 线型风格:"));    //创建线型风格选择控件
        styleComboBox =new QComboBox;
        styleComboBox->addItem(tr("SolidLine"),static_cast<int>(Qt::SolidLine));
        styleComboBox->addItem(tr("DashLine"),static_cast<int>(Qt::DashLine));
        styleComboBox->addItem(tr("DotLine"),static_cast<int>(Qt::DotLine));
        styleComboBox->addItem(tr("DashDotLine"),static_cast<int>(Qt::DashDotLine));
        styleComboBox->addItem(tr("DashDotDotLine"),static_cast<int>(Qt::DashDotDotLine));
        //关联相应的槽函数
        connect(styleComboBox, SIGNAL(activated (int)),this, SLOT(ShowStyle()));
        widthLabel =new QLabel( tr(" 线宽:"));    //创建线宽选择控件
        widthSpinBox =new QSpinBox;
        connect(widthSpinBox,SIGNAL(valueChanged(int)),drawWidget,SLOT(setWidth(int)));
        colorBtn =new QToolButton;                 //创建颜色选择控件
        QPixmap pixmap(20,20);
        pixmap.fill(Qt::black);
        colorBtn->setIcon(QIcon(pixmap));
        connect(colorBtn,SIGNAL(clicked()),this,SLOT(ShowColor()));
        clearBtn =new QToolButton();               //创建“清除”按钮
        clearBtn->setText(tr("清除"));
        connect(clearBtn,SIGNAL(clicked()),drawWidget,SLOT(clear()));
        toolBar->addWidget(styleLabel);
        toolBar->addWidget(styleComboBox);
        toolBar->addWidget(widthLabel);
        toolBar->addWidget(widthSpinBox);
        toolBar->addWidget(colorBtn);
        toolBar->addWidget(clearBtn);
    }
    void MainWindow::ShowStyle()
    {
        drawWidget->setStyle(styleComboBox->itemData(styleComboBox->currentIndex(),Qt::UserRole) .toInt());
    }
    void MainWindow::ShowColor()
    {
        QColor color= QColorDialog::getColor(static_cast<int> (Qt::black), this);
        ///使用标准颜色对话框 QColorDialog 获得一个颜色值
        if(color.isValid())
        {
            ///将新选择的颜色传给绘制区,用千改变画笔的颜色值
            drawWidget->setColor(color);
            QPixmap p(20,20);
            p. fill (color);
            colorBtn->setIcon(QIcon(p));    //更新颜色选择按钮上的颜色显示
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    四、总结

    图像与图片的双缓冲机制会在应用程序开发中经常用到的

  • 相关阅读:
    Flir Blackfly S工业相机:颜色校正讲解及配置与代码设置方法
    MySQL 根据【父ID】获取【所有子节点】
    Java+vue生成报纸排版新闻页面
    Linux驱动移植USB网卡r8156驱动(详细)总结
    StoneDB 亮相中国信通院OSCAR开源产业大会,石原子科技正式加入科技制造开源社区!
    S5PV210裸机(五):定时器
    西电算法第一次上机习题讲解
    选择题练习
    【AltWalker】模型驱动:轻松实现自动化测试用例的生成和组织执行
    WorkTool企微机器人APP分享自定义链接
  • 原文地址:https://blog.csdn.net/weixin_44759598/article/details/126810045