Einbettung von Fenstern
Eine Demonstration, wie man Nicht-Qt-UI-Elemente in Qt-Anwendungen einbettet.
Qt bietet eine breite Palette von UI-Steuerelementen sowohl für Qt Widget- als auch für Qt Quick-basierte Anwendungen, aber manchmal kann es wünschenswert sein, Steuerelemente aus anderen UI-Toolkits zu verwenden, z. B. aus dem nativen UI-Toolkit der Plattform.
Um diese Steuerelemente zu integrieren, bauen wir auf der QWindow Abstraktion in Qt auf, indem wir eine QWindow Darstellung des nativen UI-Steuerelements erstellen, die dann in die Qt UI eingebettet werden kann. Ein auf diese Weise erstelltes Fenster wird in Qt als Fremdfenster bezeichnet, da es ein Steuerelement darstellt, das von einem (für Qt) fremden UI-Toolkit erstellt wurde.
Erstellen eines fremden Fensters
Um die QWindow Repräsentation zu erstellen, verwenden wir QWindow::fromWinId() und übergeben eine Referenz auf ein natives Fensterhandle, das durch den opaken WId-Typ repräsentiert wird.
Jede Plattform definiert, auf welchen nativen Typ der opake WId-Typ abgebildet wird.
Das Ergebnis ist eine QWindow, die das native Fensterhandle darstellt.
Hinweis: Qt übernimmt bei der Erstellung eines fremden Fensters nicht den (ausschließlichen) Besitz des nativen Fensterhandles, so dass die Anwendung dafür verantwortlich ist, das native Fenster während der Lebensdauer des fremden QWindow am Leben zu erhalten.
Bevor wir nun ein QWindow mit QWindow::fromWinId() erstellen können, benötigen wir ein natives Fensterhandle. In diesem Beispiel werden wir ein Monatskalender-Steuerelement einbetten, da die meisten Plattformen es in ihren nativen UI-Toolkits oder auf andere Weise leicht verfügbar haben. Die Einzelheiten zur Erstellung des Kalenders auf den einzelnen Plattformen sind in den folgenden Codeschnipseln dargestellt.
Um sicherzustellen, dass das native Handle lebendig bleibt, aber auch beim Beenden der Anwendung ordnungsgemäß aufgeräumt wird, führen wir eine Liste von Aufräumfunktionen aus, die wir vor der Rückkehr von main()
ausführen.
Zusätzlich zur Erstellung des nativen Fensterhandles und dessen Umwandlung in ein QWindow, setzen wir auch eine Mindestgröße für das resultierende QWindow, auf deren Grundlage uns das native Toolkit die bevorzugte Mindestgröße des Kalendersteuerelements mitteilen kann. Dies ermöglicht es Qt, das eingebettete fremde Fenster richtig anzuordnen.
X11#x11#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; }
Einbetten eines fremden Fensters
Nachdem wir nun ein fremdes QWindow haben, können wir dieses in eine Qt UI einbetten. Hier haben wir mehrere Möglichkeiten, die im Folgenden beschrieben werden.
Einbettung in Qt GUI
Auf der untersten Ebene können wir das fremde Fenster einbetten, indem wir es über QWindow::setParent() in ein anderes QWindow reparentieren. Dieser Ansatz überlässt es dem Anwendungsentwickler, die Positionierung, Größenänderung und andere Aspekte der Verwaltung des eingebetteten untergeordneten Fensters zu handhaben, daher raten wir im Allgemeinen von einer Integration auf dieser Ebene ab, wenn dies möglich ist.
In diesem Beispiel erstellen wir zunächst eine minimale Containerfenster-Implementierung.
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"); } };
In dieses können wir dann unser fremdes Fenster reparieren.
ContainerWindow window; window.setTitle("Qt Gui"); auto *calendarWindow = createCalendarWindow(); calendarWindow->setParent(&window);
Einbetten in Qt Widgets
Für Anwendungen, die auf dem Qt Widgets UI-Stack aufbauen, folgen wir dem gleichen Ansatz wie für QWindow::fromWinId(), indem wir eine QWidget Repräsentation des QWindow über QWidget::createWindowContainer() erstellen.
Wir könnten dann das Widget über QWidget::setParent() in ein anderes Widget reparieren, mit den gleichen Einschränkungen wie im obigen Qt GUI-Beispiel, dass wir Positionierung, Größenänderung usw. manuell verwalten müssen. In diesem Beispiel ziehen wir es vor, das Fenster-Container-Widget zu einem QVBoxLayout hinzuzufügen, was uns erlaubt, das fremde Fenster automatisch innerhalb des Widgets der obersten Ebene zu zentrieren.
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);
Einbetten in Qt Quick
Für Anwendungen, die auf dem Qt Quick UI-Stack aufbauen, verwenden wir schließlich das Element WindowContainer, um das fremde Fenster zu verwalten.
Window { title: "Qt Quick" color: "#2CDE85" required property QtObject calendarWindow; property int contentsMargins: 20 minimumWidth: calendarWindow.minimumWidth + contentsMargins * 2 minimumHeight: calendarWindow.minimumHeight + contentsMargins * 2 WindowContainer { id: calendar window: calendarWindow width: window.minimumWidth height: window.minimumHeight anchors.centerIn: parent } }
In diesem Beispiel wird das fremde Fenster der QML-Engine als Kontexteigenschaft präsentiert, aber dies könnte je nach den Bedürfnissen der Anwendung auf unterschiedliche Weise gelöst werden.
QQmlApplicationEngine engine; engine.setInitialProperties({{ "calendarWindow", QVariant::fromValue(createCalendarWindow()) }}); engine.loadFromModule("windowembedding", "Main");
© 2025 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.