QQuickWidget grab image

2019-02-26 00:19发布

问题:

I am saving an image of a QQuickWidget with several QML children but all I have is a blank image.

C++ side:

QQuickWidget* content..
content->setSource(QUrl("qml:/main.qml"));
QPixmap *pm = content->grab(QRect(QPoint(0,0),QSize(-1,-1));
pm->save("someFilename.png", 0, 100);

QML side:

Rectangle{  width: 5; height: 5; color: "yellow"; objectname: "rootobj"}

In the QML I wish to dynamically add children and be able to show them in the image. I have tried QQuickWindow grabWindow method with a connection to a slot and it works but it captures only the window visible area and I need to capture the whole QML.

I believe this is not rocket science just that I am not getting it somewhere. Thanks for your replies!

Addendum:

Ok, I do not think its the issue of before/after rendering since I can see all the qml children before I call the picture grabber. So sorry for not being precise.

c++ side:

QQuickWidget* content..
content->setSource(QUrl("qml:/main.qml"));
//do all my dynamic qml children adding

After I can visually see all my qml:

QPixmap *pm = content->grab(QRect(QPoint(0,0),QSize(-1,-1));
pm->save(....

Unless I am wrong, I dont think its rendering issue. Thank you!

回答1:

Issue is like Mido said. You can solve it like follows.

Create a class Viewer:

viewer.h

class Viewer : public QQuickView{
    Q_OBJECT
public:
    explicit Viewer(QWindow *parent = 0);
    Viewer(bool showBar);
    virtual ~Viewer();
    void setMainQmlFile(const QString file);
    void addImportPath(const QString path);

public slots:
    void beforeRendering();
    void afterRendering()
}

Viewer.cpp

#include "viewer.h"

Viewer::Viewer(QWindow *parent)
    : QQuickView(parent)
{
    setWidth(800);
    setHeight(480);
    connect(this, SIGNAL(beforeRendering()), this, SLOT(beforeRendering()));
    connect(this, SIGNAL(afterRendering()), this, SLOT(afterRendering()));
}

void Viewer::setMainQmlFile(const QString file)
{
    setSource(QUrl::fromLocalFile(file));
}

void Viewer::addImportPath(const QString path)
{
    engine()->addImportPath(path);
}
void Viewer::beforeRendering()
{
//
}

void Viewer::afterRendering()
{
//grab window
  QImage img = this->grabWindow();
    img.save(path);
//or your code
}

main.cpp

Viewer *viewer = new Viewer;
//
///
//
viewer->setMainQmlFile(QStringLiteral("qml/main.qml"));
viewer->show();


回答2:

I think your issue is that the capture screen is done before the rendering of the QML object. In order to make it work you should connect the grab of the signal after rendering signal:

 connect(this, SIGNAL(beforeRendering()), this, SLOT(sltBeforeRendering()));
 connect(this, SIGNAL(afterRendering()), this, SLOT(sltAfterRendering()));

do the grab in sltAfterRendering slot.

To grab screen I use the grabWindow() function and I call it from QML. It depends on the behaviour that you want from your software.

Try this:

grabber.h

#ifndef GRABBER_H
#define GRABBER_H

#include <QObject>
#include <QImage>
#include <QQuickView>

class Grabber : public QObject
{
    Q_OBJECT
public:
     explicit Grabber(QObject *parent = 0);
     Grabber(QQuickView *view);
     ~Grabber();
     Q_INVOKABLE void capture(QString const &path) const;
signals:

public slots:
private:
   QQuickView*  view_;

};

#endif // GRABBER_H

grabber.cpp

#include "grabber.h"

Grabber::Grabber(QObject *parent) :
    QObject(parent)
{
}
Grabber::Grabber(QQuickView* view) :
    view_(view)
{
}
Grabber::~Grabber()
{
    if(view_ != NULL)
    {
        delete view_;
        view_ = NULL;
    }
}
void Grabber::capture(QString const &path) const
{
    QImage img = view_->grabWindow();
    img.save(path);
}

main.cpp

#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include <QQmlContext>
#include <QQmlEngine>
#include "grabber.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QtQuick2ApplicationViewer *viewer = new QtQuick2ApplicationViewer;
    Grabber * grab = new Grabber(viewer);
    viewer->setHeight(480);
    viewer->setWidth(800);

    viewer->rootContext()->setContextProperty("grab", grab);
    viewer->setMainQmlFile(QStringLiteral("qml/main.qml"));

    viewer->showExpanded();


    return app.exec();
}

Call it from QML with:

grab.capture(path + "imageName.png")