I have a QQuickWidget and want to grab a screenshot using QQuickWindow::grabWindow(). However when I do that the QQuickWindow becomes an image and is not responsive.
Below is a minimal reproducible code: The bug is reproducible in Qt5.13 to Qt5.15.1 in release mode (for some reason Qt throws an assert in debug)
//TestWidget.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets quickwidgets
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h \
windowgrabber.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
DISTFILES += \
Main.qml
RESOURCES += \
qml.qrc
//main.cpp
#include <QApplication>
#include <QQuickWidget>
#include <QQmlContext>
#include "windowgrabber.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QQuickWidget *quickWidget = new QQuickWidget;
quickWidget->rootContext()->setContextProperty("windowGrabber", new WindowGrabber(quickWidget));
quickWidget->setSource(QUrl("qrc:/Main.qml"));
quickWidget->show();
return a.exec();
}
//Main.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
Page {
Button {
id: button
text: "grab window"
onClicked: windowGrabber.grabWindow(button)
}
}
//WindowGrabber.h
#ifndef WINDOWGRABBER_H
#define WINDOWGRABBER_H
#include <QObject>
#include <QQuickItem>
#include <QQuickWindow>
class WindowGrabber : public QObject
{
Q_OBJECT
public:
WindowGrabber(QObject *parent = nullptr) : QObject(parent) {}
Q_INVOKABLE static void grabWindow(QQuickItem *item) {
item->window()->grabWindow();
}
};
#endif // WINDOWGRABBER_H
The code creates a QQuickWidget with source set to Main.qml. I want to take a screenshot when the button inside the qml is clicked. But after clicking the button the QQuickWindow inside the quickwidget becomes an image and button also becomes an image. I have tested with QWidget::createWindowContainer and it works, but the best solution would be to use a QQuickWidget. Anyone have any clues to why this might happen?
QQuickWidget uses a non-visible QQuickWindow to render the items, and that rendering is rendered again in the QWidget, so when trying to save the image it interferes, causing the lag as I have already pointed out in this post.
A possible solution is to grab directly from the QQuickWidget:
#ifndef WINDOWGRABBER_H
#define WINDOWGRABBER_H
#include <QObject>
#include <QQuickWidget>
class WindowGrabber : public QObject
{
Q_OBJECT
public:
WindowGrabber(QQuickWidget *widget): m_widget(widget) {}
Q_INVOKABLE void grabWindow() {
if(m_widget){
QPixmap img = m_widget->grab();
qDebug() << img;
}
}
private:
QPointer<QQuickWidget> m_widget;
};
#endif // WINDOWGRABBER_H
#include <QApplication>
#include <QQuickWidget>
#include <QQmlContext>
#include "windowgrabber.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QQuickWidget quickWidget;
WindowGrabber grabber(&quickWidget);
quickWidget.rootContext()->setContextProperty("windowGrabber", &grabber);
quickWidget.setSource(QUrl("qrc:/main.qml"));
quickWidget.show();
return a.exec();
}
Button {
id: button
text: "grab window"
onClicked: windowGrabber.grabWindow()
}