Reputation: 89
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:
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.
I am using qt 6.5
on windows 10.
Upvotes: 0
Views: 230
Reputation: 2363
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");
}
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
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();
}
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