You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QOpenGLWidget渲染至QPixmap时QPainter绘制内容消失问题求助

QOpenGLWidget混合渲染截图丢失QPainter内容问题解决

问题现象

使用QOpenGLWidget同时进行OpenGL原生渲染和QPainter绘制时,界面显示正常,但调用render()生成截图后:

  • QPainter绘制的内容从界面上消失,直到窗口调整大小触发重绘才恢复
  • 生成的截图中也没有QPainter绘制的元素

复现代码如下:

#include <QApplication>
#include <qopenglwidget.h>
#include <qopenglfunctions.h>
#include <qpushbutton.h>
#include <QPainter>

class MainWidget : public QOpenGLWidget, QOpenGLFunctions{
public:
    QPushButton* screenshot_button;

    MainWidget(QWidget* parent=nullptr) : QOpenGLWidget(parent){
        screenshot_button = new QPushButton("sreenshot", this);
        QObject::connect(screenshot_button, &QPushButton::clicked, [this](){
            take_screenshot();
        });
    }

    void initializeGL(){
        initializeOpenGLFunctions();
    }

    void take_screenshot(){
        QPixmap pixmap(size());
        render(&pixmap, QPoint(), QRegion(rect()));
        pixmap.save("screenshot.png");
    }

    void paintGL(){
        QPainter painter(this);

        painter.beginNativePainting();
        glClearColor(0.80, 0.80, 0.80, 1);
        glClear(GL_COLOR_BUFFER_BIT);
        painter.endNativePainting();

        // 点击截图按钮后该矩形消失!截图中也不存在
        painter.drawRect(QRect(0, 0, 100, 100));
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWidget w;
    w.show();
    return a.exec();
}

问题原因

  1. render()方法会触发一次额外的paintGL()调用,但此次调用时OpenGL上下文状态与正常绘制不一致,导致QPainter的绘制状态被破坏
  2. 直接在paintGL()中初始化QPainter(this)并混合beginNativePainting()/endNativePainting(),会干扰QPainter与OpenGL上下文的交互逻辑,后续绘制无法正确执行

解决方案

方案1:使用grab()替代render()(推荐)

grab()方法专门用于捕获部件的可视化内容,更适配QOpenGLWidget的混合渲染场景,同时手动触发重绘恢复界面状态:

修改后的代码:

#include <QApplication>
#include <qopenglwidget.h>
#include <qopenglfunctions.h>
#include <qpushbutton.h>
#include <QPainter>

class MainWidget : public QOpenGLWidget, QOpenGLFunctions{
public:
    QPushButton* screenshot_button;

    MainWidget(QWidget* parent=nullptr) : QOpenGLWidget(parent){
        screenshot_button = new QPushButton("screenshot", this);
        QObject::connect(screenshot_button, &QPushButton::clicked, [this](){
            take_screenshot();
        });
    }

    void initializeGL(){
        initializeOpenGLFunctions();
    }

    void take_screenshot(){
        // 使用grab()直接捕获部件完整内容
        QPixmap pixmap = grab();
        pixmap.save("screenshot.png");
        // 手动触发重绘,确保界面元素正常显示
        update();
    }

    void paintGL(){
        // 先执行OpenGL原生渲染,无需QPainter包裹
        glClearColor(0.80, 0.80, 0.80, 1);
        glClear(GL_COLOR_BUFFER_BIT);

        // 初始化QPainter进行2D绘制,自动处理上下文切换
        QPainter painter(this);
        painter.drawRect(QRect(0, 0, 100, 100));
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWidget w;
    w.show();
    return a.exec();
}

方案2:严格管理OpenGL上下文与QPainter状态(若需保留render())

如果必须使用render(),需在截图前后保存/恢复OpenGL上下文状态,并确保QPainter正确初始化与结束:

void take_screenshot(){
    QPixmap pixmap(size());
    // 保存当前OpenGL上下文状态
    makeCurrent();
    QOpenGLContext* ctx = context();
    ctx->saveState();

    render(&pixmap, QPoint(), QRegion(rect()));
    pixmap.save("screenshot.png");

    // 恢复上下文状态并触发重绘
    ctx->restoreState();
    doneCurrent();
    update();
}

void paintGL(){
    QPainter painter;
    // 显式begin/end管理QPainter生命周期
    painter.begin(this);

    painter.beginNativePainting();
    glClearColor(0.80, 0.80, 0.80, 1);
    glClear(GL_COLOR_BUFFER_BIT);
    painter.endNativePainting();

    painter.drawRect(QRect(0, 0, 100, 100));
    painter.end();
}

效果验证

修改后:

  • 截图可完整捕获OpenGL渲染内容与QPainter绘制元素
  • 点击截图按钮后,界面上的QPainter内容不会消失,无需手动调整窗口触发重绘

内容的提问来源于stack exchange,提问作者ali mostafavi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 03:47:04