窗口嵌入
演示如何将非 Qt UI 元素嵌入 Qt 应用程序。
Qt 为基于 Qt Widget 和Qt Quick 的应用程序提供了大量 UI 控件,但有时可能需要使用其他 UI 工具包(如平台的本地 UI 工具包)中的控件。
为了集成这些控件,我们在 Qt 的QWindow 抽象基础上,创建了本地 UI 控件的QWindow 表示形式,然后将其嵌入到 Qt UI 中。以这种方式创建的窗口在 Qt 中被称为外来窗口,因为它代表的是由外来(对 Qt 来说)用户界面工具包创建的控件。
创建外来窗口
要创建QWindow 表示法,我们使用QWindow::fromWinId() 传递一个本地窗口句柄的引用,该句柄由不透明的 WId 类型表示。
每个平台都定义了 WId 不透明类型所映射的本地类型。
结果是一个代表本地窗口句柄的QWindow 。
注意: 创建外来窗口时,Qt 不会(独占)本地窗口句柄的所有权,因此应用程序有责任在外来窗口的生命周期内保持本地窗口的活力QWindow 。
现在,在使用QWindow::fromWinId() 创建QWindow 之前,我们需要一个本地窗口句柄。在本例中,我们将嵌入一个月历控件,因为大多数平台的本地 UI 工具包中都有该控件,或者有其他现成的控件。在各个平台上创建日历的具体方法见下面的代码片段。
为确保本机句柄保持激活状态,并在应用程序退出时进行适当清理,我们维护了一个清理函数列表,在从main()
返回之前执行这些函数。
除了创建本机窗口句柄并将其转化为QWindow 之外,我们还为生成的QWindow 设置了最小尺寸,本机工具包可据此告诉我们日历控件的首选最小尺寸。这样,Qt 就能正确布局嵌入式外来窗口。
X11 iOS#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; }
嵌入外来窗口
现在我们有了一个外来QWindow ,可以将其嵌入到 Qt UI 中。我们有以下几种选择。
嵌入 Qt GUI
在最底层,我们可以通过QWindow::setParent() 将外来窗口重新代入另一个QWindow ,从而将其嵌入。这种方法会让应用程序开发人员自行处理定位、调整大小以及管理嵌入子窗口的其他方面,因此我们通常建议尽可能不要在此级别上进行集成。
在本示例中,我们首先创建一个最小的容器窗口实现。
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"); } };
然后,我们就可以将外来窗口重定向到该容器中。
ContainerWindow window; window.setTitle("Qt Gui"); auto *calendarWindow = createCalendarWindow(); calendarWindow->setParent(&window);
嵌入Qt Widgets
对于在Qt Widgets UI 栈上构建的应用程序,我们采用与QWindow::fromWinId() 相同的方法,通过QWidget::createWindowContainer() 创建QWindow 的QWidget 表示。
然后,我们可以通过QWidget::setParent() 将该窗口小部件重定向到另一个窗口小部件中,注意事项与上述 Qt GUI 示例相同,即必须手动管理定位、调整大小等。在本例中,我们更倾向于将窗口容器部件添加到QVBoxLayout 中,这样就可以自动将外来窗口居中放置在顶层部件中。
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);
嵌入Qt Quick
最后,对于在Qt Quick UI 栈上构建的应用程序,我们使用WindowContainer 项来管理外来窗口。
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 } }
在本例中,外来窗口是作为上下文属性暴露给 QML 引擎的,但根据应用程序的需要,可以用不同的方法解决这个问题。
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.