《Qt6开发及实例》6-3 双缓冲机制

目录

一、原理与设计

1.1 原理

1.2 设计

二、绘图区的实现

2.1 鼠标移动事件

2.2 重绘函数&调整大小函数&清除屏幕

三、主窗口的实现

3.1 代码


一、原理与设计

1.1 原理

双缓冲就是在绘制控件时,将内容绘制在一个图片中,再将图片一次性地绘制到控件上。直接在控件上绘制会产生闪烁的现象,控件重绘频繁,闪烁更明显。QT6 版的 QWidget 控件已经能够自动处理闪烁,但是双缓冲仍有用武之地。当需要绘制的内容较复杂并需要频繁刷新,或者每次只需要刷新整个控件的一小部分时,仍尽量采用双缓冲机制

1.2 设计

界面

《Qt6开发及实例》6-3 双缓冲机制_第1张图片

 

 两个类

《Qt6开发及实例》6-3 双缓冲机制_第2张图片

建立项目 

《Qt6开发及实例》6-3 双缓冲机制_第3张图片

《Qt6开发及实例》6-3 双缓冲机制_第4张图片

二、绘图区的实现

2.1 鼠标移动事件

《Qt6开发及实例》6-3 双缓冲机制_第5张图片

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 = 0);
    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

 drawwidget.cpp

#include "drawwidget.h"
#include 

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::mousePressEvent(QMouseEvent *e)
{
    startPos = e->pos();
}

// QWidget的mouseTracking属性指示窗体是否追踪,默认为false(不追踪),即只有按下鼠标左键后移动
// 才触发mouseMoveEvent事件。我们可以使用setMouseTracking(bool enable)方法对该属性值进行
// 设置,如果为true(追踪),那鼠标无论是否点击都会触发mouseMoveEvent事件。在此函数中,我们完成
// 向QPixmap对象中绘图的工作
void DrawWidget::mouseMoveEvent(QMouseEvent *e)
{
    QPainter *painter = new QPainter;
    QPen pen;
    pen.setStyle((Qt::PenStyle)style);  //(a)
    pen.setWidth(weight);               //设置画笔的线宽值
    pen.setColor(color);                //设置画笔的颜色
    painter->begin(pix);                //(b)
    painter->setPen(pen);               //将QPen对象应用到绘图对象中
    // 绘制从startPos到鼠标当前位置的直线
    painter->drawLine(startPos, e->pos());
    painter->end();
    startPos = e->pos();                //更新鼠标的当前位置,为下次绘制做准备
    update();                           //重新绘制区窗体
}

void DrawWidget::paintEvent(QPaintEvent *)
{

}

void DrawWidget::resizeEvent(QResizeEvent *)
{

}

// 接收主窗口传来的线型风格参数
void DrawWidget::setStyle(int s)
{
    style = s;
}

// 接收主窗口传来的线宽参数值
void DrawWidget::setWidth(int w)
{
    weight = w;
}

// 接收主窗口传来的画笔颜色值
void DrawWidget::setColor(QColor c)
{
    color = c;
}

void DrawWidget::clear()
{

}

(a) pen.setStyle((Qt::PenStyle)style);

上面的函数是设置画笔的线宽。style 是类的成员变量 (int style;),表示当前选择的线型是 Qt::PenStyle 枚举数据中的第几个元素,其他函数可以设置这个值

(b) painter->begin(pix)、painter->end()

Qt 的绘图操作,是使用 QPainter 在 paintevent() 函数中进行的,所有绘图操作都要放进函数 paintevent() 中。

在实际编程中,例如编写计算机图形学作业——编写简易绘图库时,为了封装便利,需要将绘图操作从 paintevent() 中外提。这时候 QPixmap 便派上用场了:在 paintevent() 之外,将所有绘图操作绘制在 QPixmap上,而在paintevent() 之内,仅绘制 QPixmap 即可。

(11条消息) Qt5 绘图 - 利用 QPixmap 和 QPainter 实现在 paintevent() 函数外绘图_sigmarising的博客-CSDN博客_qpainter qpixmaphttps://blog.csdn.net/sigmarising/article/details/79920380

2.2 重绘函数&调整大小函数&清除屏幕

重绘函数 paintEvent() 完成绘制区窗体的更新工作,只需要调用 drawPixmap() 函数将用于接收图形绘制的 QPixmap 对象绘制在绘图区窗体控件上

调整绘制区大小后执行 resizeEvent()

void DrawWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.drawPixmap(QPoint(0, 0), *pix);
}

void DrawWidget::resizeEvent(QResizeEvent *event)
{
    if(height() > pix->height() || width() > pix->width())  //(a)
    {
        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);                            //完成其余的工作
}

(a) if(height() > pix->height() || width() > pix->width())

判断改变后的窗体长或宽是否大于原窗体的长或宽。若大于则进行相应的调整,否则直接调用 QWidget 的 resizeEvent() 函数返回 

清除屏幕

// 清除屏幕
void DrawWidget::clear()
{
    QPixmap *clearPix = new QPixmap(size());
    clearPix->fill(Qt::white);
    pix = clearPix;
    update();
}

三、主窗口的实现

3.1 代码

《Qt6开发及实例》6-3 双缓冲机制_第6张图片

mainwindow.h 

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include 
#include 
#include 
#include 
#include "drawwidget.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~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

 mainwindow.cpp

#include "mainwindow.h"
#include 
#include 

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(Qt::SolidLine));
    styleComboBox->addItem(tr("DashLine"), static_cast(Qt::DashLine));
    styleComboBox->addItem(tr("DotLine"), static_cast(Qt::DotLine));
    styleComboBox->addItem(tr("DashDotLine"), static_cast(Qt::DashDotLine));
    styleComboBox->addItem(tr("DashDotDotLine"), static_cast(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(Qt::black), this);  //使用标准颜色对话框QColorDialog获得一个颜色值
    if(color.isValid())               //将新选择的颜色传给绘制区,用于改变画笔的颜色值
    {
        drawWidget->setColor(color);
        QPixmap p(20, 20);
        p.fill(color);
        colorBtn->setIcon(QIcon(p));  //更新颜色选择按钮上的颜色显示
    }
}

程序存在问题一个是中文字体是乱码,我们可以在 main 里面修改字体,但是我发现界面里面的字还是乱码,这个先不解决。

《Qt6开发及实例》6-3 双缓冲机制_第7张图片

程序还存在一个问题就是其他风格的线弄不出来,因为每次隔很短的距离就要绘制一次。我不清楚作者在用 QT6 的时候能否显示其他风格的线

你可能感兴趣的:(QT,ui)