You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:16:53