ali mostafavi
ali mostafavi

Reputation: 89

QPainter effects were removed when rendering a QOpenGLWidget into a QPixmap

I have a QOpenGLWidget that does some OpenGL rendering and some non-OpenGL rendering using a QPainter. The widget looks OK but when I try to create a screenshot of the widget by rendering it into a QPixmap, suddenly all effects that are painted using QPainter disappear. Here is a minimal code sample to reproduce the issue:

#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();

        // this disappears when the screenshot button is pressed!
        // also it is not present in the screenshot
        painter.drawRect(QRect(0, 0, 100, 100));
    }
};

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

Before pressing the button the widget looks normal: screenshot1.png

But after pressing the screenshot button, the rectangle disappears from the widget (also it is absent from screenshot.png) until I resize the window which forces a re-render. screenshot.png

I am using qt 6.5 on windows 10.

Upvotes: 0

Views: 230

Answers (2)

user17726418
user17726418

Reputation: 2363

Solution 1:

From QScreen::grabWindow() documentation:

The grabWindow() function grabs pixels from the screen, not from the window, i.e. if there is another window partially or entirely over the one you grab, you get pixels from the overlying window, too.

Meaning it grabs from the framebuffer unlike QWidget::grab and QWidget::render which ask the widget to render itself.

So it could be used instead of render() to avoid the problems caused by combining the latter with OpenGL:

void take_screenshot()
{     
    QGuiApplication::primaryScreen()->grabWindow(winId()).save("screenScreenshot.png");
}

Solution 2:

Less straightforward but just in case render is a must.

Use a bool to disable openGL when calling render in take_screenshot(), and make sure you call grabFramebuffer() before.

void take_screenshot()
{
    grabFramebuffer();

    QPixmap pixmap(size());

    enable_opengl=false;
    render(&pixmap, QPoint(), QRegion(rect()));
    enable_opengl=true;

    pixmap.save("renderScreenshot.png");
}

void paintGL()
{
    QPainter painter(this);

    if(enable_opengl)
    {
        glClearColor(0.80, 0.80, 0.80, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    }

    painter.fillRect(QRect(10, 10, 100, 100), Qt::green);
}

Upvotes: 0

Parisa.H.R
Parisa.H.R

Reputation: 3908

Try This

#include <QApplication>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QPushButton>
#include <QPainter>

class MainWidget : public QOpenGLWidget, protected 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() {
        QImage image = grabFramebuffer(); // Get the screenshot as QImage
        QPixmap pixmap = QPixmap::fromImage(image); // Convert QImage to QPixmap
        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();
}


enter image description here

It seems you're encountering a common issue related to mixing OpenGL rendering and QPainter-based rendering in a Qt application. The problem arises from the order in which the painting operations are performed and the interaction between QPainter and the OpenGL context. To address this issue, you need to ensure that the QPainter-based rendering and the OpenGL rendering are synchronized properly.

The problem you're facing is that when you take a screenshot using the render() function, the OpenGL rendering and the QPainter-based rendering are not synchronized. The OpenGL rendering is performed in the paintGL() function, and the QPainter-based rendering is done afterward in the take_screenshot() function.

To resolve this issue, you should make sure to synchronize the rendering by using the QOpenGLWidget's grabFramebuffer() function. This function returns a QImage with the current OpenGL framebuffer content, which includes both OpenGL and QPainter-based rendering. I convert the QImage to a QPixmap before saving it.

By using the grabFramebuffer() function, you capture the current state of the OpenGL framebuffer, including any OpenGL-rendered content as well as the QPainter-based content. This ensures that both types of rendering are synchronized and included in the screenshot.

Upvotes: 0

Related Questions