如何在QML应用中嵌入显示Windows原生OpenGL窗口?
在QML应用中嵌入Windows原生OpenGL窗口的方案总结
需求与约束
我在开发QML应用时需要实现显示一个Windows原生窗口的功能,同时要满足以下约束条件:
- 原生窗口采用OpenGL绘制
- 该窗口仅用于显示,无需响应用户输入
- 禁止使用第三方库
- 可访问原生窗口的源代码,但不建议将其移植到
QOpenGLWidget
可行方案:嵌入原生窗口并同步位置
经过尝试,利用QWindow::fromWinId和QWidget::createWindowContainer将原生窗口嵌入QWidget,再让这个Widget同步QML元素的尺寸与位置是可行的方案。
实现步骤与代码示例
1. QML端配置
在main.qml中,把用于定位的Rectangle传入自定义的MyInterfaceObject的targetObject属性:
Rectangle { id: app // ... 其他界面代码 Rectangle { id: target // ... 目标区域的布局设置 MyInterfaceObject { targetObject: target // ... 其他属性配置 } } }
2. C++端逻辑实现
MyInterfaceObject是继承QObject的自定义类,带有QVariant类型的targetObject属性。它会先获取原生窗口句柄,将窗口嵌入Widget,再通过定时器同步Widget与QML元素的位置和尺寸:
// 通过窗口标题查找原生窗口句柄 HWND windowHandle = ::FindWindow(0, L"Your Window Title"); // 将原生窗口转换为QWindow并创建容器Widget QWindow *embeddedWidgetWindow = QWindow::fromWinId((WId)windowHandle); // quickWidget是main.cpp中创建的QQuickWidget实例 QWidget *embeddedWidget = QWidget::createWindowContainer(embeddedWidgetWindow, quickWidget); // 显示相关窗口 embeddedWidget->show(); embeddedWidgetWindow->show(); quickWidget->show(); // 启动定时器同步位置与尺寸 QTimer* timer = new QTimer; timer->setInterval(1); timer->start(); connect(timer, &QTimer::timeout, [this](){ // 获取QML中的目标元素 auto quickItem = this->targetObject().value<QQuickItem*>(); if (!quickItem) return; // 计算Widget的目标位置(映射QML元素的场景坐标) auto scenePos = quickItem->mapToItem(nullptr, quickItem->position()); embeddedWidget->setGeometry(scenePos.x(), scenePos.y(), quickItem->width(), quickItem->height()); });
3. 主程序配置(main.cpp)
创建QQuickWidget作为容器,并让MyInterfaceObject可以访问它:
QQuickWidget* quickWidget = new QQuickWidget; quickWidget->setSource(QUrl(QStringLiteral("qrc:/main.qml"))); quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); // 让MyInterfaceObject可以访问该容器Widget MyInterfaceObject::setQuickWidget(quickWidget);
方案局限
该方案能让嵌入的Widget成功跟随QML元素,但当目标元素放在Flickable上时,滚动或拖拽操作会出现短暂的延迟。
不可行方案:屏幕抓取方式
尝试过使用QScreen::grabWindow结合QQuickPaintedItem的方案——在每个绘制事件中抓取原生窗口的图像并绘制到QML元素上,但该方案对目标OpenGL窗口无效,只能得到空白图像。
代码示例
// 在QQuickPaintedItem的paint事件中执行 HWND windowHandle = ::FindWindow(0, L"Your Window Title"); auto quickItem = this->targetObject().value<QQuickItem*>(); // 尝试抓取窗口图像(或抓取嵌入后的Widget的winId) auto pixmap = QGuiApplication::primaryScreen()->grabWindow((WId)windowHandle); painter->drawPixmap(QRect(0, 0, quickItem->width(), quickItem->height()), pixmap, pixmap.rect());
补充尝试
还尝试过结合BitBlt和QtWin::fromHBITMAP的方式来获取窗口图像,但同样对目标OpenGL窗口无效,得到的仍是空白图像。
内容的提问来源于stack exchange,提问作者kañe
相关产品推荐
相关产品推荐

