diff options
author | Laszlo Agocs <[email protected]> | 2014-03-19 09:21:11 +0100 |
---|---|---|
committer | Laszlo Agocs <[email protected]> | 2014-07-01 14:20:57 +0200 |
commit | 27bbd51698c12891475dc27cb742265128e098d6 (patch) | |
tree | 854469b11471a9ca7fc3555e9af2737084735cc4 | |
parent | 08f9b552c3735e15f9bf58ab5908652b06e47e88 (diff) |
Add grabFramebuffer() to QQuickWidget and use it in the autotest
This introduces the need for a grab function in QQuickWidget.
The render control has one already so there is no reason for not exposing
this in QQuickWidget too.
This also means that a relatively meaningful autotest can be now be added.
[ChangeLog][QtQuick] Added QQuickWidget::grabFramebuffer() for capturing the content into a QImage.
Task-number: QTBUG-37589
Change-Id: I5ca8192c0ef8dab4f076a4db27b64aebe3359bb8
Reviewed-by: Gunnar Sletta <[email protected]>
-rw-r--r-- | src/quick/items/qquickrendercontrol.cpp | 2 | ||||
-rw-r--r-- | src/quickwidgets/qquickwidget.cpp | 15 | ||||
-rw-r--r-- | src/quickwidgets/qquickwidget.h | 4 | ||||
-rw-r--r-- | tests/auto/auto.pro | 2 | ||||
-rw-r--r-- | tests/auto/quickwidgets/qquickwidget/data/animating.qml | 11 | ||||
-rw-r--r-- | tests/auto/quickwidgets/qquickwidget/data/error1.qml | 5 | ||||
-rw-r--r-- | tests/auto/quickwidgets/qquickwidget/data/rectangle.qml | 8 | ||||
-rw-r--r-- | tests/auto/quickwidgets/qquickwidget/data/resizemodeitem.qml | 7 | ||||
-rw-r--r-- | tests/auto/quickwidgets/qquickwidget/qquickwidget.pro | 19 | ||||
-rw-r--r-- | tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp | 276 | ||||
-rw-r--r-- | tests/auto/quickwidgets/quickwidgets.pro | 2 |
11 files changed, 351 insertions, 0 deletions
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index 311a1b95f8..3a287242e0 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -317,6 +317,8 @@ void QQuickRenderControl::render() /*! Grabs the contents of the scene and returns it as an image. + + \note Requires the context to be current. */ QImage QQuickRenderControl::grab() { diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index dc6b872fb2..fc6d39e766 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -1089,4 +1089,19 @@ QSurfaceFormat QQuickWidget::format() const return d->offscreenWindow->format(); } +/*! + Renders a frame and reads it back into an image. + + \note This is a potentially expensive operation. + */ +QImage QQuickWidget::grabFramebuffer() const +{ + Q_D(const QQuickWidget); + if (!d->context) + return QImage(); + + d->context->makeCurrent(d->offscreenSurface); + return d->renderControl->grab(); +} + QT_END_NAMESPACE diff --git a/src/quickwidgets/qquickwidget.h b/src/quickwidgets/qquickwidget.h index 4287933063..6c3d63a419 100644 --- a/src/quickwidgets/qquickwidget.h +++ b/src/quickwidgets/qquickwidget.h @@ -47,6 +47,7 @@ #include <QtCore/qurl.h> #include <QtQml/qqmldebug.h> #include <QtQuickWidgets/qtquickwidgetsglobal.h> +#include <QtGui/qimage.h> QT_BEGIN_NAMESPACE @@ -64,6 +65,7 @@ class Q_QUICKWIDGETS_EXPORT QQuickWidget : public QWidget Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(QUrl source READ source WRITE setSource DESIGNABLE true) Q_ENUMS(ResizeMode Status) + public: explicit QQuickWidget(QWidget *parent = 0); QQuickWidget(QQmlEngine* engine, QWidget *parent); @@ -92,6 +94,8 @@ public: void setFormat(const QSurfaceFormat &format); QSurfaceFormat format() const; + QImage grabFramebuffer() const; + public Q_SLOTS: void setSource(const QUrl&); void setContent(const QUrl& url, QQmlComponent *component, QObject *item); diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 715f3e01ad..d37e4a57ed 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -9,6 +9,8 @@ SUBDIRS=\ cmake \ installed_cmake +qtHaveModule(widgets): SUBDIRS += quickwidgets + qmldevtools.CONFIG = host_build installed_cmake.depends = cmake diff --git a/tests/auto/quickwidgets/qquickwidget/data/animating.qml b/tests/auto/quickwidgets/qquickwidget/data/animating.qml new file mode 100644 index 0000000000..1f6467aabe --- /dev/null +++ b/tests/auto/quickwidgets/qquickwidget/data/animating.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +Rectangle { + Rectangle { + width: 100 + height: 100 + anchors.centerIn: parent + color: "red" + NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; } + } +} diff --git a/tests/auto/quickwidgets/qquickwidget/data/error1.qml b/tests/auto/quickwidgets/qquickwidget/data/error1.qml new file mode 100644 index 0000000000..09df679555 --- /dev/null +++ b/tests/auto/quickwidgets/qquickwidget/data/error1.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Rectangle { + nonExistentProperty: 5 +} diff --git a/tests/auto/quickwidgets/qquickwidget/data/rectangle.qml b/tests/auto/quickwidgets/qquickwidget/data/rectangle.qml new file mode 100644 index 0000000000..3b18ba35c1 --- /dev/null +++ b/tests/auto/quickwidgets/qquickwidget/data/rectangle.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +Rectangle { + width: 200 + height: 200 + + color: "red" +} + diff --git a/tests/auto/quickwidgets/qquickwidget/data/resizemodeitem.qml b/tests/auto/quickwidgets/qquickwidget/data/resizemodeitem.qml new file mode 100644 index 0000000000..225c5711a9 --- /dev/null +++ b/tests/auto/quickwidgets/qquickwidget/data/resizemodeitem.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 +Item { + width: 200 + height: 200 + +} + diff --git a/tests/auto/quickwidgets/qquickwidget/qquickwidget.pro b/tests/auto/quickwidgets/qquickwidget/qquickwidget.pro new file mode 100644 index 0000000000..069270da3c --- /dev/null +++ b/tests/auto/quickwidgets/qquickwidget/qquickwidget.pro @@ -0,0 +1,19 @@ +CONFIG += testcase +TARGET = tst_qquickwidget +SOURCES += tst_qquickwidget.cpp + +include (../../shared/util.pri) + +osx:CONFIG -= app_bundle + +QT += core-private gui-private qml-private quick-private quickwidgets quickwidgets-private widgets-private testlib + +TESTDATA = data/* + +OTHER_FILES += \ + animating.qml \ + error1.qml \ + rectangle.qml \ + resizemodeitem.qml + +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp new file mode 100644 index 0000000000..db2d0e14e4 --- /dev/null +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QtTest/QSignalSpy> +#include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlcontext.h> +#include <QtQuick/qquickview.h> +#include <QtQuick/qquickitem.h> +#include "../../shared/util.h" +#include <QtGui/QWindow> +#include <QtCore/QDebug> +#include <QtQml/qqmlengine.h> + +#include <QtQuickWidgets/QQuickWidget> + +class tst_qquickwidget : public QQmlDataTest +{ + Q_OBJECT +public: + tst_qquickwidget(); + +private slots: + void showHide(); + void reparentAfterShow(); + void changeGeometry(); + void resizemodeitem(); + void errors(); + void engine(); + void readback(); +}; + + +tst_qquickwidget::tst_qquickwidget() +{ +} + +void tst_qquickwidget::showHide() +{ + QWidget window; + + QQuickWidget *childView = new QQuickWidget(&window); + childView->setSource(testFileUrl("rectangle.qml")); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window, 5000)); + + childView->hide(); +} + +void tst_qquickwidget::reparentAfterShow() +{ + QWidget window; + + QQuickWidget *childView = new QQuickWidget(&window); + childView->setSource(testFileUrl("rectangle.qml")); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window, 5000)); + + QScopedPointer<QQuickWidget> toplevelView(new QQuickWidget); + toplevelView->setParent(&window); + toplevelView->setSource(testFileUrl("rectangle.qml")); + toplevelView->show(); + QVERIFY(QTest::qWaitForWindowExposed(&window, 5000)); +} + +void tst_qquickwidget::changeGeometry() +{ + QWidget window; + + QQuickWidget *childView = new QQuickWidget(&window); + childView->setSource(testFileUrl("rectangle.qml")); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window, 5000)); + + childView->setGeometry(100,100,100,100); +} + +void tst_qquickwidget::resizemodeitem() +{ + QWidget window; + window.setGeometry(0, 0, 400, 400); + + QScopedPointer<QQuickWidget> view(new QQuickWidget); + view->setParent(&window); + view->setResizeMode(QQuickWidget::SizeRootObjectToView); + QCOMPARE(QSize(0,0), view->initialSize()); + view->setSource(testFileUrl("resizemodeitem.qml")); + QQuickItem* item = qobject_cast<QQuickItem*>(view->rootObject()); + QVERIFY(item); + window.show(); + + view->showNormal(); + // initial size from root object + QCOMPARE(item->width(), 200.0); + QCOMPARE(item->height(), 200.0); + QCOMPARE(view->size(), QSize(200, 200)); + QCOMPARE(view->size(), view->sizeHint()); + QCOMPARE(view->size(), view->initialSize()); + + // size update from view + view->resize(QSize(80,100)); + + QTRY_COMPARE(item->width(), 80.0); + QCOMPARE(item->height(), 100.0); + QCOMPARE(view->size(), QSize(80, 100)); + QCOMPARE(view->size(), view->sizeHint()); + + view->setResizeMode(QQuickWidget::SizeViewToRootObject); + + // size update from view disabled + view->resize(QSize(60,80)); + QCOMPARE(item->width(), 80.0); + QCOMPARE(item->height(), 100.0); + QTRY_COMPARE(view->size(), QSize(60, 80)); + + // size update from root object + item->setWidth(250); + item->setHeight(350); + QCOMPARE(item->width(), 250.0); + QCOMPARE(item->height(), 350.0); + QTRY_COMPARE(view->size(), QSize(250, 350)); + QCOMPARE(view->size(), QSize(250, 350)); + QCOMPARE(view->size(), view->sizeHint()); + + // reset window + window.hide(); + view.reset(new QQuickWidget(&window)); + view->setResizeMode(QQuickWidget::SizeViewToRootObject); + view->setSource(testFileUrl("resizemodeitem.qml")); + item = qobject_cast<QQuickItem*>(view->rootObject()); + QVERIFY(item); + window.show(); + + view->showNormal(); + + // initial size for root object + QCOMPARE(item->width(), 200.0); + QCOMPARE(item->height(), 200.0); + QCOMPARE(view->size(), view->sizeHint()); + QCOMPARE(view->size(), view->initialSize()); + + // size update from root object + item->setWidth(80); + item->setHeight(100); + QCOMPARE(item->width(), 80.0); + QCOMPARE(item->height(), 100.0); + QTRY_COMPARE(view->size(), QSize(80, 100)); + QCOMPARE(view->size(), view->sizeHint()); + + // size update from root object disabled + view->setResizeMode(QQuickWidget::SizeRootObjectToView); + item->setWidth(60); + item->setHeight(80); + QCOMPARE(view->width(), 80); + QCOMPARE(view->height(), 100); + QCOMPARE(QSize(item->width(), item->height()), view->sizeHint()); + + // size update from view + view->resize(QSize(200,300)); + QTRY_COMPARE(item->width(), 200.0); + QCOMPARE(item->height(), 300.0); + QCOMPARE(view->size(), QSize(200, 300)); + QCOMPARE(view->size(), view->sizeHint()); + + window.hide(); + + // if we set a specific size for the view then it should keep that size + // for SizeRootObjectToView mode. + view.reset(new QQuickWidget(&window)); + view->resize(300, 300); + view->setResizeMode(QQuickWidget::SizeRootObjectToView); + QCOMPARE(QSize(0,0), view->initialSize()); + view->setSource(testFileUrl("resizemodeitem.qml")); + view->resize(300, 300); + item = qobject_cast<QQuickItem*>(view->rootObject()); + QVERIFY(item); + window.show(); + + view->showNormal(); + + // initial size from root object + QCOMPARE(item->width(), 300.0); + QCOMPARE(item->height(), 300.0); + QTRY_COMPARE(view->size(), QSize(300, 300)); + QCOMPARE(view->size(), view->sizeHint()); + QCOMPARE(view->initialSize(), QSize(200, 200)); // initial object size +} + +void tst_qquickwidget::errors() +{ + QQuickWidget *view = new QQuickWidget; + QScopedPointer<QQuickWidget> cleanupView(view); + + QQmlTestMessageHandler messageHandler; + view->setSource(testFileUrl("error1.qml")); + QVERIFY(view->status() == QQuickWidget::Error); + QVERIFY(view->errors().count() == 1); +} + +void tst_qquickwidget::engine() +{ + QScopedPointer<QQmlEngine> engine(new QQmlEngine); + QScopedPointer<QQuickWidget> view(new QQuickWidget(engine.data(), 0)); + QScopedPointer<QQuickWidget> view2(new QQuickWidget(view->engine(), 0)); + + QVERIFY(view->engine()); + QVERIFY(view2->engine()); + QCOMPARE(view->engine(), view2->engine()); +} + +void tst_qquickwidget::readback() +{ +#ifdef Q_OS_MAC + QSKIP("Skipping due to issues on OS X: QTBUG-39919"); +#endif + + QWidget window; + + QScopedPointer<QQuickWidget> view(new QQuickWidget); + view->setSource(testFileUrl("rectangle.qml")); + + view->show(); + QVERIFY(QTest::qWaitForWindowExposed(view.data(), 5000)); + + QImage img = view->grabFramebuffer(); + QVERIFY(!img.isNull()); + QCOMPARE(img.width(), view->width()); + QCOMPARE(img.height(), view->height()); + + QRgb pix = img.pixel(5, 5); + QCOMPARE(pix, qRgb(255, 0, 0)); +} + +QTEST_MAIN(tst_qquickwidget) + +#include "tst_qquickwidget.moc" diff --git a/tests/auto/quickwidgets/quickwidgets.pro b/tests/auto/quickwidgets/quickwidgets.pro new file mode 100644 index 0000000000..ee8d6077e6 --- /dev/null +++ b/tests/auto/quickwidgets/quickwidgets.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += qquickwidget |