Sign up for the KDAB Newsletter
Stay on top of the latest news, publications, events and more.
Go to Sign-up
12 June 2025
Developing an application for desktop or embedded platforms often means choosing between Qt Widgets and Qt Quick to develop the UI. There are pros and cons to each. Qt, being the flexible framework that it is, lets you combine these in various ways. How you should integrate these APIs will depend on what you're trying to achieve. In this entry I will show you how to display Qt Widget windows on an application written primarily using Qt Quick.
Qt Quick is great for software that puts emphasis on visual language. A graphics pipeline, based around the Qt Quick Scene Graph, will efficiently render your UI using the GPU. This means UI elements can be drawn, decorated, and animated efficiently as long as you pick the right tools (e.g. Shaders, Animators, and Qt's Shapes API instead of its implementation of HTML's Canvas).
From the Scene Graph also stem some of Quick's weaknesses. UI elements that in other applications would extend outside of the application's window, such as tool tips and the ComboBox control, can only be rendered inside of Qt Quick windows. When you see other app's tooltips and dropdowns extend beyond the window, those items are being rendered onto a separate windows; one without window decorations (a.k.a. borderless windows). Rendering everything on the same window helps ensure your app will be compatible with systems that can only display a single window at a time, such as Android and iOS, but it could result in wasted space if your app targets PC desktop environments.
An animation shows a small window with QML's and Widget's ComboBoxes opening for comparison purposes
QML ComboBox is confined to the Qt Quick window while the Widgets ComboBox extends beyond the window
Qt lets us combine Widgets and Quick in a few ways. The most common approach is to embed a Qt Quick view into your Widgets app, using QQuickWidget. That approach is fitting for applications that primarily use Widgets. Another option is to render Widgets inside a Qt Quick component, by rendering it through a QQuickPaintedItem. However, this component will be limited to the same window confines as the rest of the items in your Quick window and it won't benefit from Scene Graph rendering optimizations, meaning you get the worst of both worlds.
A third solution is to open widget windows from your Qt Quick apps. This has none of the aforementioned drawbacks, however, the approach has a couple of drawback of its own. First, the app would need to be run from a multi-window per screen capable environment. Second, widget windows are not parentable to Qt Quick windows; meaning certain window z-stack related features, such as setting window modality to Qt::WindowModal, won't have effect on the triggering window when a Widget is opened from Qt Quick. You can work around that by setting modality to Qt::ApplicationModal instead, if you're okay with blocking all other windows for modality.
Displaying Widget windows in Qt Quick applications has been useful to me in the past, and is something I haven't seen documented anywhere, hence this tutorial.
Displaying a Qt Widget window from Qt Quick is simpler than it seems. You'll need two classes:
You might be tempted to forgo the interface class and instantiate the widget directly. However, this would result in a crash. We'll display the widget window by running Widget::show
from the interface class.
CMakeLists.txt
In addition to those classes, you'll also need to make sure that your app links to both Qt::Quick
and Qt::Widgets
libraries. Here's what that looks like for a CMake project
// Locate libraries
find_package(Qt6 6.5 REQUIRED COMPONENTS
Quick
Widgets)
// Link build target to libraries
target_link_libraries(${TARGET_NAME} PRIVATE
Qt6::Quick
Qt6::Widgets)
// Replace ${TARGET_NAME} with the name of your target executable
main.cpp
In addition to that, in main.cpp
you'll need to use QApplication in place of QGuiApplication.
QApplication app(argc, argv);
Prepare the interface layer as you would any C++ based Quick component. By this I mean: derive from QObject
, and use the Q_OBJECT
and QML_ELEMENT
macros to make your class available from QML.
// widgetFormHandler.h
#pragma once
class WidgetFormHandler : public QObject
{
Q_OBJECT
QML_ELEMENT
public:
explicit WidgetFormHandler(QObject *parent = nullptr);
};
// widgetFormHandler.cpp
WidgetFormHandler::WidgetFormHandler(QObject *parent)
: QObject(parent)
{
}
// widgetFormHandler.h
#pragma once
class WidgetsForm;
class WidgetFormHandler : public QObject
{
Q_OBJECT
QML_ELEMENT
public:
explicit WidgetFormHandler(QObject *parent = nullptr);
~WidgetFormHandler();
private:
std::unique_ptr<WidgetsForm> m_window;
}
Use std::make_unique
in the constructor to initialize the unique pointer to m_window.
Define the instantiating class' destructor to ensure the pointers are de-alocated, thus preventing memory leaks. If you stick to using smart pointers, C++ will do all the work for you; simply use the default destructor, like I do here. Make sure to define it outside of the class' header; some compilers have trouble dealing with the destructor when it's defined inside the header.
// widgetFormHandler.cpp
#include "widgetFormHandler.h"
WidgetFormHandler::WidgetFormHandler(QObject *parent)
: QObject(parent)
, m_window(std::make_unique<WidgetsForm>())
{
// ...
}
WidgetFormHandler::~WidgetFormHandler() = default;
Now we want to make properties from the widget available in QML. How we do this will depend on the property and on whether we will manipulate the property's value from both directions or only from one side only and update on the other.
Let's look at a bi-directional example in which we add the ability to control the visible state of the widget window from QML. We'll add a property called "visible" to the C++ interface so that it matches the visible that we get from Qt Quick windows in QML. Declare the property using Q_PROPERTY
. Use READ and WRITE functions to control the window's state.
Here's what that would look like:
// widgetFormHandler.h
#pragma once
class WidgetsForm;
class WidgetFormHandler : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
public:
explicit WidgetFormHandler(QObject *parent = nullptr);
~WidgetFormHandler();
const bool isVisible();
void setVisible(bool);
signals:
void visibleChanged();
private:
std::unique_ptr<WidgetsForm> m_window;
};
// widgetFormHandler.cpp
#include "widgetFormHandler.h"
#include "widgetForm.h"
WidgetFormHandler::WidgetFormHandler(QObject *parent)
: QObject(parent)
, m_window(std::make_unique<WidgetsForm>())
{
// Hide window by default
m_window->setVisible(false);
}
WidgetFormHandler::~WidgetFormHandler() = default;
const bool WidgetFormHandler::isVisible()
{
return m_window->isVisible();
}
void WidgetFormHandler::setVisible(bool visible)
{
m_window->setVisible(visible);
emit visibleChanged();
}
To make this bi-directional, set NOTIFY to a signal that allows the property to be updated in QML after it being emitted and emit the signal where applicable. We emit it from setVisible in this class, however if QWidget
had a signal that emitted when its visible state changed, I would also make a connection between that signal and that of our handler’s visibleChanged
. However, that isn’t the case, so we have to make sure to emit it ourselves.
Develop the widget window as you would any other widget. If you use UI forms, go to the header file and create a signal for each action that you wish to relay over to QML.
In this example we'll relay a button press from the UI file, so we'll create a button named pushButton in our ui file:
Qt Designer shows UI file with a button named pushButton, in camel case.
Now add a buttonClicked
signal to our header:
// widgetsForm.h
#pragma once
#include <QWidget>
namespace Ui
{
class WidgetsForm;
}
class WidgetsForm : public QWidget
{
Q_OBJECT
public:
explicit WidgetsForm(QWidget *parent = nullptr);
~WidgetsForm();
signals:
void buttonClicked();
// Signal to expose button click from Widgets window
private:
std::unique_ptr<Ui::WidgetsForm> ui;
};
Once again, we use a unique pointer, this time to hold the ui object. This is better than what Qt Creator templates give us because it means C++ handles the memory management for us and we can avoid the need for a delete statement in the destructor.
In the window's constructor, we make a connection between the UI's button's signal and the one that we've created to relay the signal for exposure.
// widgetsForm.cpp
#include "widgetsform.h"
#include "ui_widgetsform.h"
WidgetsForm::WidgetsForm(QWidget *parent)
: QWidget(parent)
, ui(std::make_unique<Ui::WidgetsForm>())
{
ui->setupUi(this);
// Expose click
connect(ui->pushButton, &QPushButton::clicked, this, &WidgetsForm::buttonClicked);
}
WidgetsForm::~WidgetsForm() = default;
Before we connect the exposed signal to the QML interface, we need another signal on the interface to expose our event over to QML. Here I add qmlSignalEmitter
signal for that purpose:
// widgetFormHandler.h
[..]
signals:
void visibleChanged();
void qmlSignalEmitter(); // Signal to relay button press to QML
[..]
To complete all the connections, go to the interface layer’s constructor and make a connection between your window class’ signal and that of the interface layer. This would look as follows:
// widgetFormHandler.cpp
[..]
WidgetFormHandler::WidgetFormHandler(QObject *parent)
: QObject(parent)
, m_window(std::make_unique<WidgetsForm>())
{
QObject::connect(m_window, &WidgetsForm::buttonClicked, this,
&WidgetFormHandler::qmlSignalEmitter);
}
[..]
By connecting one emitter to another emitter we keep each classes' concerns separate and reduce the amount of boilerplate code, making our code easier to maintain.
Over at the QML, we connect to qmlSignalEmitter
using the on prefix. It would look like this:
import NameOfAppQmlModule // Should match qt_add_qml_module's URI on CMake
WidgetFormHandler {
id: fontWidgetsForm
visible: true // Make the Widgets window visible from QML
onQmlSignalEmitter: () => {
console.log("Button pressed in widgets") // Log QPushButton's click event from QML
}
}
I've prepared a demo app where you can see this technique in action. The demo displays text that bounces around the screen like an old DVD player's logo would. You change the text and font through two identical forms, one implemented in QML and the other done in Widgets. The code presented in this tutorial comes from that demo app.
Example code: https://github.com/KDABLabs/kdabtv/tree/master/Blog-projects/Widget-window-in-Qt-Quick-app
The moving text should work on all desktop systems except for Wayland sessions on Linux. That is because I'm animating the window's absolute position (which is restricted in Wayland for security reasons) rather than the contents inside a window. This has the benefit of not obstructing other applications, since the moving window that contains the text would capture mouse inputs if clicked, preventing those from reaching the application behind it.
The first time I employed this technique was in my FOSS project, QPrompt. I use it there to provide a custom font dialog that doubles as a text preview. Having a custom dialog gives me full control over formatting options presented to users, and for this app we only needed a preview for large text and a combo box to choose among system fonts. QPrompt is also open source, you can find the source code relevant to this technique here: https://github.com/Cuperino/QPrompt-Teleprompter/blob/main/src/systemfontchooserdialog.h
Thank you for reading. I hope you’ll find this useful. A big thank you to David Faure for suggesting the use of C++ unique pointers, and to him, Renato and my team for reviewing the code.
If there are other techniques that you’d like for us to try or showcase, let us know.
About KDAB
The KDAB Group is a globally recognized provider for software consulting, development and training, specializing in embedded devices and complex cross-platform desktop applications. In addition to being leading experts in Qt, C++ and 3D technologies for over two decades, KDAB provides deep expertise across the stack, including Linux, Rust and modern UI frameworks. With 100+ employees from 20 countries and offices in Sweden, Germany, USA, France and UK, we serve clients around the world.
Stay on top of the latest news, publications, events and more.
Go to Sign-up
Upgrade your applications from Qt 5 to Qt 6 with KDAB’s migration services. Get a free migration assessment and join a hands-on workshop to prepare your team for a successful transition!
Learn more
Learn Modern C++
Our hands-on Modern C++ training courses are designed to quickly familiarize newcomers with the language. They also update professional C++ developers on the latest changes in the language and standard library introduced in recent C++ editions.
Learn more