<< to CrossControl homepage

Support & Service Center

Using QML inside a QtWidgets project

Printer-friendly versionPrinter-friendly versionPDF versionPDF version

Brief

QtWidgets and QML does not use the same graphical backend and can not normally be combined. However, there are workarounds for situations where it is a requirement. One such workaround is described in this document. The QQuickWidget.

The QQuickWidget

General

Normally QML is run on a scenegraph that is in control of the OpenGL pipeline that owns the frame buffer. Typically initialized by a QQuickView or similar “root” class for QML. This is why it doesn’t combine natively with QWidgets that use their own, different, framebuffer connection. There is one QWidget that gets around this limitation, albeit with a performance hit – the QQuickWidget. It does the same QML initialization as a QQuickView with the exception that instead of giving QML control of the framebuffer, QML is given an OpenGL Framebuffer Object (FBO for short) as render target. This FBO is then used by the QQuickWidget to draw itself first (more on that later) in the normal widget pipeline.  

Limitations

  • The use of an FBO causes a performance hit since the texels (“pixels” not yet on the framebuffer) must be effectively copied an extra time.
  • In the case of iMX5 platforms, use of a FBO may be a showstopper. There is a bug that potentially (yet to be fully investigated) crashes the GPU when non-tiny FBOs or PBOs are used.
  • Threaded render loops are not allowed. This limits the performance benefits of certain components, such as Animator classes and vsync driven animations.
  • Due to how QQuickWidgets have to draw themselves before other widgets, its QML content can not use transparency to reveal widgets below, so partially “see-through” QML components are not possible.
    Note: Widgets can still go on top of the QML content as normal.
  • The point above can be worked around by designating the QQuickWidget as always-stack-on-top (attribute Qt::AlwaysStackOnTop). This however, merely reverses the limitation since stacking order is broken and no other widgets can go above the QML instead.
Note: Since it is the QML buffer itself that needs to be transparent we need to enable alpha for it. This is done by setting the attribute Qt::WA_TranslucentBackground on the top-level QML Window, request an alpha channel, and change the QML scenegraph’s clear color to Qt::transparent (setClearColor(Qt::transparent));

Usage

Essentially, QQuickWidgets are added just like any other widgets. There are a few things to keep in mind though.
  1. They reside in their own QT module that needs to be included in the .pro
    QT += quickwidgets
  2. If added in a form or through the designer, do not set the source property directly if any rootContexts need to be available during QML initialization.
    QML content will be initialized when the source is set and by default this is done when the QQuickWidget itself is created.
  3. In the constructor that adds the QQuickWidget – i.e. the widget that has it in its UI file or manually adds it:
  4. (If rootContext access is needed) Include
  5. Register any custom QML classes before or after the QQuickWidget is initialized but before any QML source is loaded.
  6. After the QQuickWidget has been created but before its source is set, register any rootContext properties or objects in its QML view.
  7. Only after this is all done, set its source to your root QML file to initialize the QML content itself.
  8. Finally, with all components initialized, create any signal/slot connections as needed between the QML content and the rest of your widget application

Example code

#include "ui_application.h"
#include <QQmlContext>
 
Application::Application(QWidget *parent)
    : QWidget(parent), ui(new Ui::Application) {
 
  // QML engine is spun up by the quickwidget's constructor, i.e. during setupUi.
  qmlRegisterType<FPSText>("CrossControl", 1, 0, "FPSText");
  qmlRegisterType<CircularMeterGL>("CrossControl", 1, 0, "CircularMeter");
  ui->setupUi(this);
 
  // RootContexts must be added after setupUI, but before qml source is set.
  // So if the quickwidget is in a form, the source property must not be set
  // there. It must only be set after we've had the chance to register
  // rootProperties in the engine, which we can only do after the form has been
  // processed and there is a qml engine to access.
  ui->quickWidget->rootContext()->setContextProperty("externalSlider",
                                                     ui->horizontalSlider);
  ui->quickWidget->rootContext()->setContextProperty("externalCheckbox",
          <                                           ui->checkBox);
 
  // Now we can set the source
  ui->quickWidget->setSource(QUrl("qrc:/main.qml"));
 
  // QML is fully initialized. We can now set up connections to it
  connect(ui->pushButtonClose, &QPushButton::click, this, &QApplication::quit);
}
Category: