QT之绘图模块和双缓冲技术

在这里插入图片描述

首先先创建顶部的工具栏

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setWindowTitle("图形绘制综合案例分析(双缓冲机制)");
    drawWidget=new DrawWidget;
    setCentralWidget(drawWidget); // 将刚才创建对象作为主窗口的中心窗口

    CreateToolBarFunc(); // 调用此函数实现创建工具栏

    setMinimumSize(600,400);

    dispstyle();

    drawWidget->setWidth(spinboxlabelwidth->value()); // 初始化线宽度
    drawWidget->setColor(Qt::black); // 初始化线颜色


}

void MainWindow::CreateToolBarFunc() // 创建工具条
{
    QToolBar *toolBar=addToolBar("Tool");
    labelstyle=new QLabel("线型风格:");
    comboboxlabelstyle=new QComboBox;
    //添加四种画画使用的线形
    comboboxlabelstyle->addItem("SolidLine",static_cast<int>(Qt::SolidLine)); // 实线
    comboboxlabelstyle->addItem("DashLine",static_cast<int>(Qt::DashLine)); //
    comboboxlabelstyle->addItem("DashDotDotLine",static_cast<int>(Qt::DashDotDotLine));
    comboboxlabelstyle->addItem("DotLine",static_cast<int>(Qt::DotLine)); // 虚线

    connect(comboboxlabelstyle,SIGNAL(activated(int)),this,SLOT(dispstyle()));

    labelwidth=new QLabel("线型宽度:");
    spinboxlabelwidth=new QSpinBox;
    connect(spinboxlabelwidth,SIGNAL(valueChanged(int)),drawWidget,SLOT(setWidth(int)));

    //选择画笔颜色按钮
    colorbutton=new QToolButton;
    QPixmap pixmap(20,20);
    pixmap.fill(Qt::black);
    colorbutton->setIcon(QIcon(pixmap));

    connect(colorbutton,&QToolButton::clicked, [this](){

        QColor color=QColorDialog::getColor(static_cast<int>(Qt::black),this);
        if(color.isValid())
        {
            drawWidget->setColor(color);
            //在Lambda内部创建新的QPixmap,确保修改后的颜色能正确应用到按钮图标。
            QPixmap pix(20,20);
            pix.fill(color);
            colorbutton->setIcon(QIcon(pix));//确保颜色改变后按钮图标实时更新。
        }
    });

    clearbutton=new QToolButton;
    clearbutton->setText("清除绘制");
    connect(clearbutton,SIGNAL(clicked()),drawWidget,SLOT(clearFunc()));

//将前面做好的控件都添加到工具栏
    toolBar->addWidget(labelstyle);
    toolBar->addWidget(comboboxlabelstyle);
    toolBar->addWidget(labelwidth);
    toolBar->addWidget(spinboxlabelwidth);
    toolBar->addWidget(colorbutton);
    toolBar->addWidget(clearbutton);
}


void MainWindow::dispstyle()
{
    //取组合框(QComboBox)里当前所选条目的用户数据,并且把它转换为整数类型
    drawWidget->setStyle(comboboxlabelstyle->itemData(comboboxlabelstyle->currentIndex(),
                                                      Qt::UserRole).toInt());



}

创建绘图区域

单缓冲的痛点​​:
直接在前台缓冲区(屏幕)绘制时,若绘制操作未在屏幕刷新周期内完成,用户会看到绘制中间过程,导致闪烁。

双缓冲技术(Double Buffering)是图形编程中用于 ​​消除绘制闪烁​​ 和 ​​提升渲染性能​​ 的核心方法

  • 所有绘制操作先在后台缓冲区完成
  • 再一次性将后台内容复制到前台缓冲区
#include "drawwidget.h"

DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent)
{
    //设置控件是否自动填充背景
    setAutoFillBackground(true);
    setPalette(QPalette(Qt::white));
    pix=new QPixmap(size());
    pix->fill(Qt::white);

    // 设置绘制区窗口最小尺寸
    setMinimumSize(600,400);
}

void DrawWidget::clearFunc() // 清除函数
{
    QPixmap *cPix=new QPixmap(size());
    cPix->fill(Qt::white);
    pix=cPix;
/*
当你调用update()时,实际上并没有马上对界面进行重绘,而是会在 Qt 的事件循环里添加一个重绘事件。
等到下一次处理重绘事件时,Qt 会自动调用paintEvent()函数,从而完成界面的更新。
这种机制具备合并多个重绘请求的能力,能够有效避免界面出现闪烁现象
*/
    update();​​//不直接处理绘制逻辑​​,只是触发 paintEvent 调用
}

void DrawWidget::mousePressEvent(QMouseEvent *e)
{
    //QPoint在二维平面上确定一个点的位置,也就是存储坐标
    startpos=e->pos();//返回鼠标事件发生时,鼠标指针相对于接收事件的控件(widget)的坐标位置。

}


//双缓冲
//在mouseMoveEvent中,创建了一个QPainter对象,
//然后调用begin方法传入一个QPixmap指针(pix),设置画笔,绘制线条,然后结束绘制。
//之后调用update()来触发paintEvent。
//在paintEvent中,他们创建了一个QPainter对象,将QPixmap绘制到窗口上。

void DrawWidget::mouseMoveEvent(QMouseEvent *e)//默认情况下,只有鼠标按键按下后移动才会触发 mouseMoveEvent
//若调用 setMouseTracking(true) 会强制开启无按钮跟踪,此时需通过按钮状态判断
{
    QPainter painter(pix);
    QPen pen;//优化成成员变量,避免频繁创建
    pen.setStyle((Qt::PenStyle)style);
    pen.setWidth(widthss);
    pen.setColor(color);

    //启动在指定 QPaintDevice(如 QWidget、QPixmap、QImage 等)上的绘制操作
    painter.begin(pix);
    painter.setPen(pen);
    painter.drawLine(startpos,e->pos());
    painter.end();

    // 计算需要更新的区域
    //QRect在二维平面上表示矩形区域,QRect(topLeft, bottomRight)通过左上角和右下角两个点来确定矩形
    //normalized():该方法的作用是确保 QRect 对象的宽度和高度都是正值
    //adjusted():将矩形区域在所有方向扩展 2 像素,确保覆盖画笔边缘
    QRect updateRect = QRect(startpos, e->pos()).normalized().adjusted(-2, -2, 2, 2);
    
    // 更新起点为当前位置(为下一次移动准备)
    startpos=e->pos();
    update(updateRect); // 局部刷新,仅重绘受影响区域

}

/*
 * 双缓冲技术
在mouseMoveEvent中直接在pix(QPixmap)上绘制,这属于后台缓冲区操作。
paintEvent中将整个pix绘制到窗口,这属于将后台缓冲区复制到前台。
✅ 所有动态绘制操作在 QPixmap(后台缓冲区)上完成	​​
✅ paintEvent 仅负责将后台缓冲区复制到屏幕
✅ 通过 update() 触发统一刷新
*/
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())
    {
        QPixmap *newPix=new QPixmap(size());
        newPix->fill(Qt::white);//新画布填充白色

        QPainter ps(newPix);

        //将旧画布内容叠加到新画布上。
        ps.drawPixmap(QPoint(0,0),*pix);
        //delete pix; 会导致原来的画在缩小尺寸以后被清除
        pix=newPix;
    }

    QWidget::resizeEvent(event);

}

void DrawWidget::setStyle(int s) // 设置线风格
{
    style=s;
}

void DrawWidget::setWidth(int w) // 设置线宽度
{
    widthss=w;
}

void DrawWidget::setColor(QColor c) // 设置线颜色
{
    color=c;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值