Qt动态绘图 双缓冲区 paintEvent个人理解

本文围绕QT动态绘图展开,介绍了paintEvent事件的触发情况,如鼠标操作和update()执行后触发。作者结合单片机经验对QT事件触发机制进行理解,还阐述了双缓冲区绘图可避免闪烁,最后给出按键绘图、清除及鼠标拖动绘图的实现内容和代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.调试过程中可知,鼠标每次到ui时候  会触发paintEvent事件

        我把这次触发叫做 空跑   因为它没有执行你想操作的事件  比如鼠标,按键等 

        paintEvent的触发   

            (1)鼠标选中  或者在ui上晃悠的时候触发

            (2)update()  执行后  这个事件已经触发  但是要执行万当前函数的代码 才会到paintEvent中执行你的代码,这个来源于实际的调试经验。

2.由于本人单片机经验,对QT的事件触发做一下理解

       一开始将事件触发当作是中断

       只要有事件发生  它就要跑到对应的函数中进行操作

       但实际中  需要将本个函数块中的执行完毕  在跑到对应的事件触发函数中

       对此  个人理解为  本个函数块中 有触发其他函数块的事件  QT先把这个时间记录下来  然后 执行完本函数块之后   再执行对应的触发事件。

3.双缓冲区绘图

        双缓冲区  绘图时,将所有内容绘制到一个绘图设备(QPixmap),再将整个图像绘制到部件上显示出来

        使用双缓冲区绘图 可以避免绘图时的闪烁现象,

        具体应用:鼠标拖动绘制矩形的过程中

4 实现内容   按键按下  自动绘制矩形   按下对应按键  清除画板上的内容  鼠标拖动绘制矩形

      

 

头文件

#ifndef WIDGET_H
#define WIDGET_H
#include <QPaintDevice>
#include<QPaintEngine>
#include<QPainter>
#include <QWidget>
#include <QColor>
#include <QPixmap>
#include <QPoint>
#include <QMouseEvent>
namespace Ui {
class Widget;
}

 
class Widget : public QWidget
{
    Q_OBJECT

 
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

 
private:
    Ui::Widget *ui;
    QPixmap Pix;

 
    QPixmap tempPix;//临时缓冲区
    QPoint startPoint;
    QPoint endPoint;
    bool isDrawing;//是否正在绘图的标志`
    bool clear_Flag;//清空标志

 

 
public:
    void paintEvent(QPaintEvent *event);
    void draw_point(int const x,int const y,QColor const c,int const w);
    void mouseMoveEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);

 

 
private slots:
    void on_beginpushButton_clicked();
    void on_clearpushButton_2_clicked();
};

 
#endif // WIDGET_H

cpp 源代码

#include "widget.h"
#include "ui_widget.h"
//可以去看Qcustomer
#define WIN_WIGHT 200
#define WIN_HEIGHT 200
#define WIN_TITLE "画图测试"
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //初始化QPixmap
    Pix = QPixmap(WIN_WIGHT,WIN_HEIGHT);
    Pix.fill(Qt::white);
    tempPix=Pix;
    isDrawing=false;
}

 
Widget::~Widget()
{
    delete ui;
}

 
void Widget::paintEvent(QPaintEvent *event)
{
    //QPainter painter(this);  //表示在当前的窗口绘制图形  会自动的开始和结束
    //painter.drawLine(QPoint(0,0),QPoint(100,100));

 
   // QPainter painter;//没有指定窗口  需要自己写 begin  end  和上面那段代码d等效
    //painter.begin(this);//this 当前窗口的指针  //QPainter::QPainter ( QPaintDevice * device)
    //painter.drawLine(QPoint(0,0),QPoint(100,100));
    //painter.end();

 

 
    int x=startPoint.x();
    int y=startPoint.y();
    int width=endPoint.x()-x;
    int height=endPoint.y()-y;
    //如果有清空标志
    if(clear_Flag==1)
    {
        x=0;
        y=0;
        width=0;
        height=0;
    }
    QPainter painter;
    painter.begin(&tempPix);
    painter.drawRect(x,y,width,height);
    painter.end();

 
    painter.begin(this);
    painter.drawPixmap(0,0,tempPix);

 

 
    //如果已经完成了绘制 更新缓冲区
    if(!isDrawing){
        Pix=tempPix;
    }

 

 
}

 

 
void Widget::mousePressEvent(QMouseEvent *event){
    if(event->button()==Qt::LeftButton){
        clear_Flag=0;
        //鼠标按下左键 获取当前位置作为矩形的开始点
       startPoint =event->pos();
        isDrawing=true;
    }
}
void Widget::mouseReleaseEvent(QMouseEvent *event){
    if(event->button()==Qt::LeftButton){
        clear_Flag=0;
        //当鼠标左键松开时候  获取当前位置为结束点
        endPoint=event->pos();
        //标记已经绘制结束
        isDrawing=false;
        update();
    }
}

 
void Widget::mouseMoveEvent(QMouseEvent *event){
    if(event->buttons()&Qt::LeftButton){
        clear_Flag=0;
        //按下鼠标左键进行移动  获取当前位置为结束点 绘制矩形
        endPoint=event->pos();
        //将缓冲区内容复制到临时缓冲区,进行动态绘图
        //每次都在缓冲区的图像的基础上进行绘图 这样不会出现重影
        tempPix=Pix;
        //更新显示
        update();
    }
}

 

 

 
void Widget::on_beginpushButton_clicked()
{
    clear_Flag=0;
    int x=qrand()%100;
    int y=qrand()%100;
    //draw_point(x,y,QColor(10,20,20),10);
    startPoint.setX(0);
    startPoint.setY(0);
    isDrawing=true;
    endPoint.setX(x);
    endPoint.setY(y);
    tempPix=Pix;
    isDrawing=false;
    update();
    clear_Flag=0;
}

 

 
void Widget::on_clearpushButton_2_clicked()
{
   clear_Flag=1;
   Pix.fill(Qt::white);
   tempPix=Pix;
   update();
}

最后致谢:霍亚飞

  附:本人新手小白  搞上面这个大概花了5-10 个小时  功能基本实现

         上面描述中个人理解如有错误   请大神斧正!

<think>嗯,用户想知道在Qt中如何实现双绘图操作,还希望有一个示例。首先,我需要回忆一下Qt绘图机制。记得Qt在4.0之后默认所有部件都自使用双冲了,所以可能不需要手处理?不过用户可能还是需要自定义双冲的情况,比如处理复杂的绘图或者避免某些闪烁问题。 根据引用[2],Qt默认已经用了双冲,所以一般情况下不需要自己实现。但用户可能是在旧版本或者有特殊需求的情况下需要手操作。这时候,正确的步骤应该是创建一个离屏的绘图设备,比如QPixmap,然后在上面绘制,最后再把这个Pixmap绘制到部件上。 接下来要考虑示例代码的结构。用户可能需要一个简单的例子,比如在paintEvent中先绘制到QPixmap,再用QPainter画到部件上。需要确保代码正确,使用Q_OBJECT宏,继承QWidget,重写paintEvent方法。另外,可能需要定时器来触发更新,这样可以看到画效果,比如移一个圆形的位置。 然后要注意代码中的关键点:创建QPixmap作为冲区,使用QPainter在QPixmap上绘制,最后将QPixmap绘制到窗口上。同时,需要注意内存管理,确保每次绘制前清空冲区,比如用fill填充背景色。 可能还需要解释为什么这样做可以避免闪烁,因为所有绘图操作先在内存完成,然后一次性绘制到屏幕,减少了屏幕更新的次数。此外,引用[1]中的示例也提到了使用双冲后绘图更平滑,避免闪烁,这和用户的疑问相关。 另外,用户可能对QPixmap和QImage的区别有疑问,但根据引用[3],QPixmap适合在屏幕上显示,而QImage用于处理图像数据。所以在示例中使用QPixmap作为冲区是正确的选择。 最后,要确保回答结构清晰,分步骤说明实现方法,并提供完整的代码示例。同时,生成相关问题,帮助用户深入理解冲的应用和优化方法。比如,如何优化复杂图形的绘制,或者双冲的应用场景有哪些。</think>### Qt绘图实现方法分步说明 1. **理解冲原理** - 双冲通过先在内存设备(QPixmap)完成绘制,再一次性输出到屏幕显示,减少界面闪烁[^2] - 现代Qt版本默认启用双冲,但复杂场景仍需手控制 2. **创建自定义绘图部件** ```cpp #include <QWidget> #include <QPixmap> #include <QPainter> #include <QTimer> class DoubleBufferWidget : public QWidget { Q_OBJECT public: DoubleBufferWidget(QWidget *parent = nullptr) : QWidget(parent) { // 初始化定时器实现画效果 QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, [this](){ angle += 5; // 更新旋转角度 update(); // 触发重绘 }); timer->start(50); } protected: void paintEvent(QPaintEvent *) override { // 创建绘图设备 QPixmap buffer(size()); buffer.fill(Qt::white); // 清空冲区 // 第一阶段:冲绘制 QPainter bufferPainter(&buffer); drawRotatingShape(bufferPainter); // 第二阶段:屏幕绘制 QPainter screenPainter(this); screenPainter.drawPixmap(0, 0, buffer); } private: int angle = 0; void drawRotatingShape(QPainter &painter) { painter.setRenderHint(QPainter::Antialiasing); painter.translate(width()/2, height()/2); painter.rotate(angle); painter.setBrush(Qt::blue); painter.drawEllipse(-50, -50, 100, 100); } }; ``` 3. **关键实现要素** - **冲设备创建**:`QPixmap buffer(size())`创建与窗口等大的绘制表面 - **离屏绘制**:所有图形操作在`bufferPainter`上执行 - **最终绘制**:`screenPainter.drawPixmap`实现原子性屏幕输出 - **画机制**:通过定时器驱连续更新(示例中的旋转画) 4. **优化技巧** - 使用`setRenderHint(QPainter::Antialiasing)`提升绘制质量 - 对静态内容可存QPixmap避免重复绘制 - 复杂场景可分图层绘制,最后合成到冲设备
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值