Exemple d'hébergement de fenêtres
Une démonstration de la manière d'héberger des éléments d'interface utilisateur non-Qt dans des applications Qt.
Qt propose une large gamme de contrôles d'interface utilisateur pour les applications basées sur Qt Widgets et sur Qt Quick, mais il peut parfois être souhaitable d'utiliser des contrôles provenant d'autres boîtes à outils d'interface utilisateur, telles que la boîte à outils native de la plate-forme.
Pour intégrer ces contrôles, nous nous appuyons sur l'abstraction « QWindow » de Qt, en créant une représentation « QWindow » du contrôle d'interface utilisateur natif, qui peut ensuite être hébergée au sein de l'interface utilisateur Qt. Une fenêtre créée de cette manière est appelée « fenêtre étrangère » dans Qt, car elle représente un contrôle créé par une bibliothèque d'interfaces utilisateur étrangère (à Qt).
Création d’une fenêtre étrangère
Pour créer la représentation « QWindow », nous utilisons la fonction ` QWindow::fromWinId()`, en lui transmettant une référence à un descripteur de fenêtre natif, représenté par le type opaque `WId`.
Chaque plateforme définit à quel type natif le type opaque WId est mappé.
| Plateforme | Type WId |
|---|---|
| macOS | NSView* |
| Windows | HWND |
| X11 | xcb_window_t |
| iOS | UIView* |
| Android | Afficher |
| WebAssembly | emscripten::val* |
Le résultat est un objet ` QWindow ` qui représente le descripteur de la fenêtre native.
Remarque : Qt ne prend pas la propriété (exclusive) du descripteur de fenêtre natif lors de la création d’une fenêtre étrangère ; l’application est donc chargée de maintenir la fenêtre native active pendant toute la durée de vie de l’ QWindow.
Avant de pouvoir créer une fenêtre « QWindow » à l’aide de la fonction ` QWindow::fromWinId()`, nous avons besoin d’un descripteur de fenêtre native. Dans cet exemple, nous allons héberger un contrôle de calendrier mensuel, car la plupart des plateformes l’intègrent dans leurs kits d’outils d’interface utilisateur natifs ou le proposent facilement. Les détails de la création du calendrier sur chaque plateforme sont présentés dans les extraits de code ci-dessous.
Pour garantir que le descripteur natif reste actif, tout en étant correctement libéré à la fermeture de l’application, nous tenons à jour une liste de fonctions de nettoyage que nous exécutons avant de quitter la méthode ` main()`.
Outre la création du descripteur de fenêtre natif et sa conversion en ` QWindow`, nous définissons également une taille minimale pour l’ QWindow résultante, en fonction de la taille minimale recommandée pour le contrôle de calendrier indiquée par la bibliothèque native. Cela permet à
à Qt de disposer correctement la fenêtre externe hébergée.

#include <AppKit/NSDatePicker.h>
#include <AppKit/NSLayoutConstraint.h>
QWindow *createCalendarWindow()
{
auto *datePicker = [NSDatePicker new];
cleanupFunctions.push_back([=]{ [datePicker release]; });
datePicker.datePickerStyle = NSDatePickerStyleClockAndCalendar;
datePicker.datePickerElements = NSDatePickerElementFlagYearMonthDay;
datePicker.drawsBackground = YES;
datePicker.dateValue = [NSDate now];
auto *calendarWindow = QWindow::fromWinId(WId(datePicker));
calendarWindow->setMinimumSize(QSizeF::fromCGSize(datePicker.fittingSize).toSize());
return calendarWindow;
}
#include <windows.h>
#include <commctrl.h>
QWindow *createCalendarWindow()
{
static bool initializedDateControl = []{
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_DATE_CLASSES;
return InitCommonControlsEx(&icex);
}();
Q_ASSERT(initializedDateControl);
HWND monthCalendar = CreateWindow(MONTHCAL_CLASSW,
nullptr, MCS_NOTODAYCIRCLE | MCS_NOTODAY, 0, 0, 0, 0,
nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
cleanupFunctions.push_back([=]{ DestroyWindow(monthCalendar); });
auto *calendarWindow = QWindow::fromWinId(WId(monthCalendar));
RECT minimumSize;
MonthCal_GetMinReqRect(monthCalendar, &minimumSize);
const auto dpr = calendarWindow->devicePixelRatio();
calendarWindow->setMinimumSize(QSize(
minimumSize.right / dpr,minimumSize.bottom / dpr));
return calendarWindow;
}
#include <gtk/gtk.h>
#include <gtk/gtkx.h>
QWindow *createCalendarWindow()
{
static bool initializedGTK = []{
qputenv("GDK_BACKEND", "x11");
return gtk_init_check(nullptr, nullptr);
}();
Q_ASSERT(initializedGTK);
auto *plug = gtk_plug_new(0);
g_signal_connect(GTK_WIDGET(plug), "delete-event", G_CALLBACK(+[]{
return true; // Don't destroy on close
}), nullptr);
cleanupFunctions.push_back([=]{ gtk_widget_destroy(GTK_WIDGET(plug)); });
auto *calendar = gtk_calendar_new();
gtk_container_add(GTK_CONTAINER(plug), GTK_WIDGET(calendar));
gtk_widget_show_all(plug);
auto *calendarWindow = QWindow::fromWinId(gtk_plug_get_id(GTK_PLUG(plug)));
GtkRequisition minimumSize;
gtk_widget_get_preferred_size(calendar, &minimumSize, NULL);
calendarWindow->setMinimumSize(QSize(minimumSize.width, minimumSize.height));
return calendarWindow;
}
#include <UIKit/UIDatePicker.h>
QWindow *createCalendarWindow()
{
auto *datePicker = [UIDatePicker new];
cleanupFunctions.push_back([=]{ [datePicker release]; });
datePicker.datePickerMode = UIDatePickerModeDate;
datePicker.preferredDatePickerStyle = UIDatePickerStyleInline;
datePicker.backgroundColor = UIColor.systemBackgroundColor;
auto *calendarWindow = QWindow::fromWinId(WId(datePicker));
calendarWindow->setMinimumSize(QSizeF::fromCGSize(datePicker.frame.size).toSize());
return calendarWindow;
}
Q_DECLARE_JNI_CLASS(CalendarView, "android/widget/CalendarView")
Q_DECLARE_JNI_CLASS(Color, "android/graphics/Color")
QWindow *createCalendarWindow()
{
using namespace QtJniTypes;
using namespace QNativeInterface;
auto *androidApp = qGuiApp->nativeInterface<QAndroidApplication>();
Q_ASSERT(androidApp);
auto *calendarView = new CalendarView(androidApp->context());
cleanupFunctions.push_back([=]{ delete calendarView; });
// Resolving Android default colors is not trivial, so let's ask Qt
QColor paletteColor = qGuiApp->palette().color(QPalette::Window);
int backgroundColor = Color::callStaticMethod<int>("rgb",
paletteColor.red(), paletteColor.green(), paletteColor.blue());
calendarView->callMethod<void>("setBackgroundColor", backgroundColor);
auto *calendarWindow = QWindow::fromWinId(WId(calendarView->object()));
calendarWindow->setMinimumSize(QSize(200, 220));
return calendarWindow;
}
#include <emscripten.h>
#include <emscripten/val.h>
using emscripten::val;
using emscripten::EM_VAL;
EM_JS(EM_VAL, createCalendarElement, (), {
var calendar = document.createElement("calendar-date");
calendar.innerHTML = "<calendar-month></calendar-month>";
return Emval.toHandle(calendar);
});
QWindow *createCalendarWindow()
{
static bool initializedCalendarComponent = []{
return EM_ASM_INT(
var script = document.createElement('script');
script.src = "https://unpkg.com/cally";
script.type = "module";
document.head.appendChild(script);
return true;
);
}();
Q_ASSERT(initializedCalendarComponent);
val *calendarElement = new val(val::take_ownership(createCalendarElement()));
cleanupFunctions.push_back([calendarElement]{ delete calendarElement; });
QWindow *window = QWindow::fromWinId(WId(calendarElement));
window->setMinimumSize(QSize(250, 300));
return window;
}Intégration d'une fenêtre externe
Maintenant que nous disposons d'une fenêtre étrangère QWindow, nous pouvons l'intégrer dans une interface utilisateur Qt. Plusieurs options s'offrent à nous, comme décrit ci-dessous.
Intégration dans Qt GUI
Au niveau le plus bas, nous pouvons héberger la fenêtre externe en la rattachant à une autre QWindow, via la fonction QWindow::setParent(). Cette approche laisse au développeur de l’application le soin de gérer le positionnement, le redimensionnement et d’autres aspects liés à la gestion de la fenêtre enfant hébergée ; nous déconseillons donc généralement
l’intégration à ce niveau, dans la mesure du possible.
Dans cet exemple, nous créons d’abord une implémentation minimale de fenêtre conteneur.
class ContainerWindow : public QRasterWindow
{
protected:
bool event(QEvent *event) override
{
if (event->type() == QEvent::ChildWindowAdded) {
auto *childWindow = static_cast<QChildWindowEvent*>(event)->child();
childWindow->resize(childWindow->minimumSize());
setMinimumSize(childWindow->size().grownBy(contentsMargins));
resize(minimumSize());
}
return QRasterWindow::event(event);
}
void showEvent(QShowEvent *) override
{
findChild<QWindow*>()->setVisible(true);
}
void resizeEvent(QResizeEvent *) override
{
auto *containedWindow = findChild<QWindow*>();
containedWindow->setPosition(
(width() / 2) - containedWindow->width() / 2,
(height() / 2) - containedWindow->height() / 2
);
}
void paintEvent(QPaintEvent *) override
{
QPainter painter(this);
painter.fillRect(0, 0, width(), height(), "#00414A");
}
};Dans laquelle nous pouvons ensuite réaffecter notre fenêtre externe.
ContainerWindow window;
window.setTitle("Qt Gui");
auto *calendarWindow = createCalendarWindow();
calendarWindow->setParent(&window);Hébergement dans Qt Widgets
Pour les applications construites sur la pile UI Qt Widgets, nous suivons la même approche que pour QWindow::fromWinId(), en créant une représentation QWidget de l'QWindow, via QWidget::createWindowContainer().
Nous pourrions alors réaffecter le widget à un autre widget, via QWidget::setParent(), avec les mêmes précautions que dans l’exemple Qt GUI ci-dessus, à savoir la nécessité de gérer manuellement le positionnement, le redimensionnement, etc. Dans cet exemple, nous préférons ajouter le widget conteneur de fenêtre à un QVBoxLayout, ce qui nous permet de centrer automatiquement la fenêtre externe à l’intérieur du widget de niveau supérieur.
QWidget widget;
widget.setPalette(QColor("#CDB0FF"));
widget.setWindowTitle("Qt Widgets");
widget.setLayout(new QVBoxLayout);
widget.layout()->setContentsMargins(contentsMargins);
widget.layout()->setAlignment(Qt::AlignCenter);
auto *calendarWidget = QWidget::createWindowContainer(createCalendarWindow());
widget.layout()->addWidget(calendarWidget);Hébergement dans Qt Quick
Enfin, pour les applications basées sur la pile d’interface utilisateur Qt Quick, nous utilisons l’élément WindowContainer pour gérer la fenêtre externe.
Window {
id: root
title: "Qt Quick"
color: "#2CDE85"
property alias calendarWindow: calendar.window
property int contentsMargins: 20
minimumWidth: calendarWindow.minimumWidth + contentsMargins * 2
minimumHeight: calendarWindow.minimumHeight + contentsMargins * 2
WindowContainer {
id: calendar
width: window.minimumWidth
height: window.minimumHeight
anchors.centerIn: parent
}
}Dans cet exemple, la fenêtre externe est exposée au moteur QML en tant que propriété « initial », mais cela peut être résolu de différentes manières en fonction des besoins de l’application.
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "calendarWindow", QVariant::fromValue(createCalendarWindow()) }});
engine.loadFromModule("windowhosting", "Main");© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.