Combining Qt Widgets and QML with QWidget::createWindowContainer()

Jeff Tranter


Jeff Tranter is a Qt Consulting Manager at ICS. Jeff oversees the architectural and high-level design of software systems for clients. Jeff’s leadership organizes global teams of developers on desktop and embedded applications.

By Jeff Tranter | Wednesday, August 7, 2013

Introduction

Qt 5.1 introduces a new method in the QWidget class called createWindowContainer(). It allows embedding a QWindow (such as a QQuickView) into a QWidget-based application. This allows combining both QML and widgets in the same application, something that was not possible with Qt 5.0.

The new method is well documented (see References at the end of the posting) but I did not see any complete compilable examples of using it, so I thought I would present one here.

Details

The new API provides the following static method:

  QWidget *QWidget::createWindowContainer(QWindow *window, QWidget *parent=0, Qt::WindowFlags flags=0)

The window parameter can be a QQuickView, since it is derived from QWindow. The created window container will be a child of the parent widget and takes over ownership of the QWindow. The QWindow can be removed from the window container with a call to QWindow::setParent().

Once the window has been embedded into the container, it will control the window's geometry and visibility. Explicit calls to QWindow::setGeometry(), QWindow::show() or QWindow::hide() on an embedded window are not recommended.

Example

I started the example by using Qt Creator's new project wizard to create a Qt GUI application with a UI class derived from QMainWindow and a Qt Designer form. The main program, main.cpp, and window class header file mainwindow.h, were unchanged from what Qt Creator generated.

In the implementation of the UI class in mainwindow.cpp, we need to create a QQuickView and then call QWidget::createWindowContainer(), passing in the view and the parent widget. This returns a QWidget that we can add to the UI layout. I set the minimum and maximum size of the view widget and set the focus policy (see the later discussion on focus handling). Shown below is the code with the relevant lines highlighted.

#include <QQuickView>
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->pushButton, SIGNAL(clicked()), qApp, SLOT(quit()));

    QQuickView *view = new QQuickView();
    QWidget *container = QWidget::createWindowContainer(view, this);
    container->setMinimumSize(200, 200);
    container->setMaximumSize(200, 200);
    container->setFocusPolicy(Qt::TabFocus);
    view->setSource(QUrl("main.qml"));
    ui->verticalLayout->addWidget(container);
}

MainWindow::~MainWindow()
{
    delete ui;
}

The Qt designer form I created arbitrarily included a QLineEdit and a QPushButton to show some representative widgets.

I created a simple QML file which displays a red rectangle and some text. To illustrate some things that are typically done in QML I added a rotation animation of text in the rectangle, which can be paused and resumed by clicking on it.

The listing for the QML file is below. It will run as usual on its own using qmlscene if desired.

import QtQuick 2.1

Rectangle {
    id: rectangle
    color: "red"
    width: 200
    height: 200

    Text {
        id: text
        text: "This is QML code.\n(Click to pause)"
        font.pointSize: 14
        anchors.centerIn: parent
        PropertyAnimation {
            id: animation
            target: text
            property: "rotation"
            from: 0; to: 360; duration: 5000
            loops: Animation.Infinite
        }
    }
    MouseArea {
        anchors.fill: parent
        onClicked: animation.paused ? animation.resume() : animation.pause()
    }
    Component.onCompleted: animation.start()
}

A screen shot of the application running is shown below.

The complete source code for the example can be downloaded from here.

More Details

There are currently some limitations, which are described in the documentation:

  1. Stacking order: The embedded window will stack on top of the widget hierarchy as an opaque box. The stacking order of multiple overlapping window container instances is undefined.
  2. Window Handles: The window container will explicitly invoke winId() which will force the use of native window handles inside the application. See the QWidget documentation for more details.
  3. Rendering Integration: The window container does not interoperate with QGraphicsProxyWidget, QWidget::render() or similar functionality.
  4. Focus Handling: It is possible to let the window container instance have any focus policy and it will delegate focus to the window via a call to QWindow::requestActivate(). However, returning to the normal focus chain from the QWindow instance will be up to the QWindow instance implementation itself. For instance, when entering a Qt Quick based window with tab focus it is quite likely that further tab presses will only cycle inside the QML application. Also, whether QWindow::requestActivate() actually gives the window focus is platform dependent.
  5. Using many window container instances in a QWidget-based application can have a performance impact on the application.

Conclusions

The examples given in this blog post have shown how to combine a QML view with widgets in the same application. This new feature of Qt 5.1 opens up new possibilities for using QML in QWidget-based applications.

References

  1. Link to documentation
  2. Digia blog post on QWidget::createWindowContainer()


Comments

Comment: 

I tried this example in Qt5.2 for android. It builds and deploys but crashes because it cannot find main.qml.
So I tried using the Qt resource system,
view->setSource(QUrl("qrc:/main.qml"));

but now it crashes with,
W/Adreno-EGL(28315): <qeglDrvAPI_eglMakeCurrent:2918>: EGL_BAD_MATCH
E/libEGL (28315): eglMakeCurrent:775 error 3009 (EGL_BAD_MATCH)
W/Qt (28315): eglconvenience\qeglplatformcontext.cpp:119 (virtual bool QEGLPlatformContext::makeCurrent(QPlatformSurface*)): QEGLPlatformContext::makeCurrent: eglError: 3009, this: 0x74f37328
W/Qt (28315):
D/dalvikvm(28315): GC_CONCURRENT freed 422K, 5% free 9166K/9620K, paused 3ms+8672ms, total 13285ms
Debugging has finished
'org.qtproject.example.widgetsandqml' terminated.

I am trying to get my own windowcontainer app to work in android and thought this simple example would help.
But looks like there are problems here too.

Any ideas?

Comment: 

I don't currently have access to Qt in an Android device, but I;ll ask around here and see if one of my colleagues does. I saw the discussion about this on the qt-project.org forums, which lead to no solution. I suggest you ask on the android-development@qt-project.org mailing list, as more people tend to follow the mailing lists than the forums.

Comment: 

A colleague confirmed seeing a similar error on his Android device. It appears that this is not supported on Android due to a limition that Qt on Android only supports one OpenGL surface. I'm told that there has been talk about removing this restriction but there is no workaround at the moment.

Comment: 

Hi, and thanks for a great example!

I have used it as a base in my effort to gradually convert some of our software from classic widget over to qml. However we have struck on some serious problems that we need help with fixing.

Sometimes* the qml renders fine and everything is dandy.

Sometimes in this case means any combination of the following:
+ On some computers
+ On some operating systems
+ On some graphics hardware
+ Using some graphics drivers
+ Using some version of Qt

What happens when it is NOT working is that the entire qml rendering area is white. There are no error messages or crashes or anything usefull to go by except that it is rendered completely white. My hunch is that this might do some strange things that does not fare well with some buggy drivers.

I realize that this is not a problem you can give me a solution to in an instant, but what I am looking for are tips as to how we can pinpoint what happens? How can we lure out some debugging information that we can use?

Thanks!

Comment: 

I polled some of our developers about this and here are some suggestions they had:

Make sure debug output is enabled and going somewhere that you can see it.

First check if the QML view being white behavior happens with the simplest possible mixed QML/QWidget app (and not a full featured app you are working on).

See if it happens with a plain QML-only app (i.e. a bare QQmlScene or the standard Creator QML project) loading a simple QML main program.

Check the behavior when running from within Qt Creator/build environment or from a folder where every dependent library has been placed. It could be a Qt version incompatibility of some sort, with the application attempting to load the wrong libraries.

Do you do any custom OpenGL rendering of QML items, or interact with the QML scenegraph from C++ in any way?

Having the list of platforms where the application fails would be helpful if you can share it with us.