qtqmlqt5qquickwidget

QQuickWidget: grabToImage: item's window is not visible


I'm having a problem when I try to use Item::grabToImage() qml method. No matter which item I point to, it always says the following error:

grabToImage: item's window is not visible

I tried using the root/toplevel Item named rect too, but it didnt work.

My goal: I want to capture a rectangle sized image with the map tile and polygon draw on it

Below there's a minimal reproducible example

import QtQml 2.2
import QtLocation 5.9
import QtPositioning 5.9
import QtQuick 2.0
import QtQuick.Controls 2.4

Item {
    id: rect
    width: 1024
    height: 768

    visible: true

    Plugin {
        id: mapPlugin
        name: "osm"
    }

    Map {
        id: map
        enabled: true
        visible: true
        parent: rect
        gesture.enabled: true
        anchors.fill: parent
        plugin: mapPlugin
        zoomLevel: 14
        activeMapType: supportedMapTypes[3]
    }

    Item {
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.margins: 10
        height: 40

        Button {
            id: saveToDisk
            text: qsTr("Pick")
            onClicked: {
                map.grabToImage(function (result) {
                    console.log('saving to disk..')
                    result.saveToFile("pick.png")
                })
            }
        }
    }
}
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQuickWidgets/QQuickWidget>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QApplication app(argc, argv);

    QQuickWidget *q = new QQuickWidget;
    q->setResizeMode(QQuickWidget::SizeRootObjectToView);
    q->setSource(QUrl("main.qml"));
    q->show();

    return app.exec();
}

Solution

  • The strategy of QQuickWidget for painting is to create an off-screen QQuickWindow that renders the QML from where a screenshot is taken and drawn onto the widget. The above limits the use of grabToImage() since this method requires that the QQuickWindow of the items be visible.

    The solution is to use QQuickView + QWidget::createWindowContainer():

    #include <QApplication>
    #include <QWidget>
    #include <QQuickView>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QApplication app(argc, argv);
    
        QQuickView *q = new QQuickView;
        q->setResizeMode(QQuickView::SizeRootObjectToView);
        q->setSource(QUrl("main.qml"));
    
        QWidget * container = QWidget::createWindowContainer(q);
        container->show();
    
        return app.exec();
    }