QPainter效果在将QOpenGLWidget渲染到QPixmap时被移除

huangapple go评论126阅读模式
英文:

QPainter effects were removed when rendering a QOpenGLWidget into a QPixmap

问题

我有一个使用QPainter进行OpenGL渲染和非OpenGL渲染的QOpenGLWidget。小部件看起来正常,但当我尝试通过将其渲染到QPixmap中来创建小部件的屏幕截图时,所有使用QPainter绘制的效果突然消失了。以下是一个重现此问题的最小代码示例:

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

class MainWidget : public QOpenGLWidget, QOpenGLFunctions {
public:

    QPushButton* screenshot_button;

    MainWidget(QWidget* parent=nullptr) : QOpenGLWidget(parent) {
        screenshot_button = new QPushButton("截图", 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();
}

在按下按钮之前,小部件看起来正常:

QPainter效果在将QOpenGLWidget渲染到QPixmap时被移除

但在按下截图按钮后,矩形从小部件中消失(在screenshot.png中也不存在),直到我调整窗口大小以强制重新渲染。

QPainter效果在将QOpenGLWidget渲染到QPixmap时被移除

我在Windows 10上使用Qt 6.5

英文:

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 &lt;QApplication&gt;
#include &lt;qopenglwidget.h&gt;
#include &lt;qopenglfunctions.h&gt;
#include &lt;qpushbutton.h&gt;
#include &lt;QPainter&gt;

class MainWidget : public QOpenGLWidget, QOpenGLFunctions{
public:

    QPushButton* screenshot_button;

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

    void initializeGL(){
        initializeOpenGLFunctions();
    }

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

    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:
QPainter效果在将QOpenGLWidget渲染到QPixmap时被移除

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.
QPainter效果在将QOpenGLWidget渲染到QPixmap时被移除

I am using qt 6.5 on windows 10.

答案1

得分: 0

尝试这样做

#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("截图", this);
        QObject::connect(screenshot_button, &QPushButton::clicked, [this]() {
            take_screenshot();
        });
    }

    void initializeGL() {
        initializeOpenGLFunctions();
    }

    void take_screenshot() {
        QImage image = grabFramebuffer(); // 获取屏幕截图作为 QImage
        QPixmap pixmap = QPixmap::fromImage(image); // 将 QImage 转换为 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();
}

看起来你遇到了一个与在Qt应用程序中混合使用OpenGL渲染和基于QPainter的渲染相关的常见问题。问题源于绘图操作的执行顺序以及QPainter与OpenGL上下文之间的交互。要解决这个问题,你需要确保QPainter的渲染和OpenGL渲染得到正确同步。

你面临的问题是,当你使用render()函数获取截图时,OpenGL渲染和基于QPainter的渲染没有得到同步。OpenGL渲染是在paintGL()函数中执行的,而基于QPainter的渲染是在take_screenshot()函数之后执行的。

为了解决这个问题,你应该确保使用QOpenGLWidget的grabFramebuffer()函数来同步渲染。这个函数返回一个包含当前OpenGL帧缓冲内容的QImage,其中包括OpenGL和基于QPainter的渲染。

通过使用grabFramebuffer()函数,你可以捕获当前OpenGL帧缓冲的状态,包括任何OpenGL渲染的内容以及基于QPainter的内容。这确保了两种类型的渲染都在截图中得到同步。

英文:

Try This

#include &lt;QApplication&gt;
#include &lt;QOpenGLWidget&gt;
#include &lt;QOpenGLFunctions&gt;
#include &lt;QPushButton&gt;
#include &lt;QPainter&gt;

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

    MainWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) {
        screenshot_button = new QPushButton(&quot;screenshot&quot;, this);
        QObject::connect(screenshot_button, &amp;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(&quot;screenshot.png&quot;);
    }

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


QPainter效果在将QOpenGLWidget渲染到QPixmap时被移除

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.

答案2

得分: 0

Solution 1:

来自QScreen::grabWindow()文档:

> grabWindow() 函数从屏幕上获取像素,而不是从窗口上获取,也就是说,如果有另一个窗口部分或完全覆盖了您要获取的窗口,您也会获取来自上层窗口的像素。

这意味着它从帧缓冲中获取,不像 QWidget::grabQWidget::render 会要求小部件自行渲染。

因此,它可以用来替代 render(),以避免与OpenGL结合使用时可能引起的问题:

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

Solution 2:

不太直接,但以防万一需要使用 render

take_screenshot() 中调用 render 时,使用一个 bool 来禁用OpenGL,并确保在调用之前调用 grabFramebuffer()

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);
}
英文:

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()-&gt;grabWindow(winId()).save(&quot;screenScreenshot.png&quot;);
}

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(&amp;pixmap, QPoint(), QRegion(rect()));
    enable_opengl=true;

    pixmap.save(&quot;renderScreenshot.png&quot;);
}

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);
}

huangapple
  • 本文由 发表于 2023年8月5日 00:50:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76837839.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定