Combining Qt Widgets and QML with QWidget::createWindowContainer()
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:
- 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.
- 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.
- Rendering Integration: The window container does not interoperate with QGraphicsProxyWidget, QWidget::render() or similar functionality.
- 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.
- 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.