首页 文章

如何在QML应用程序中显示本机窗口?

提问于
浏览
0

问题

我需要在QML应用程序中显示的Windows本机窗口 .

约束

  • 使用OpenGL绘制本机窗口(NW)应用程序

  • NW仅用于显示目的,不需要用户输入

  • 不允许第三方图书馆

  • 您可以访问NW的源代码

  • 注意:将此特定源代码移植到QOpenGLWidget是不受欢迎的

工作解决方案

一个有效的解决方案是利用QWindow::fromWinIdQWidget::createWindowContainer方法将窗口导入QWidget . 然后我们从QML中获取一个项目并强制该小部件为"mirror"项目的尺寸和位置 .

以下代码段演示了可能的实现 .
(注意:这些代码段被解释并且没有错误检查)

main.qml

具有id“target”的Rectangle被传递到MyInterfaceObject的targetObject属性中 .

Rectangle {
    id: app
    .
    .
    Rectangle {
        id: target

        MyInterfaceObject {
            targetObject: target
            .
            .
        }
}

MyInterfaceObject.cpp

MyInterfaceObject是一个具有QVariant targetObject属性的QObject . 它需要一个所需的窗口句柄并将其“抛出”到窗口小部件上 . 然后,它启动一个计时器,将嵌入的窗口小部件映射到targetObject的位置 .

有人可能会根据某些targetObject信号而不是计时器更新,但由于我最初在Flickable上进行了测试,但是没有用 .

// Find the window by it's title
HWND windowHandle = ::FindWindow(0, L"Your Window Title");

// "Throw" window into a widget
QWindow *embeddedWidgetWindow = QWindow::fromWinId((WId)windowHandle);
// the quickWidget is created in main.cpp
QWidget *embeddedWidget = QWidget::createWindowContainer(embeddedWidgetWindow, quickWidget);

embeddedWidget->show();
embeddedWidgetWindow->show();
quickWidget->show();

// Start a timer that will enforce the "mirroring" effect
QTimer* timer = new QTimer;
timer->setInterval(1);
timer->start();

timer->connect(timer, &QTimer::timeout, [this](){
    // targetObject is the QVariant form of the target QML object from our main.qml
    auto quickItem = this->targetObject().value<QQuickItem*>();

    // Map our widgets position/dimensions to our target qml object
    auto scenePos = quickItem->mapToItem(0, quickItem->position());
    auto myX = scenePos.x() - quickItem->position().x();        
    auto myY = scenePos.x() - quickItem->position().y();

    embeddedWidget->setGeometry(myX, myY, quickItem->width(), quickItem->height());
});

main.cpp中

// The widget that will parent our native window widget
QQuickWidget* quickWidget= new QQuickWidget;
quickWidget->setSource(QUrl(QStringLiteral("qrc:/main.qml")));
quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);

// Give our InterfaceObject access to this quickWidget
InterfaceObject::setQuickWidget(quickWidget);

嵌入式小部件将成功跟踪我的QML对象 . 但是,当设置为Flickable时,小部件在滚动或拖动时会暂时滞后一点 .

非工作解决方案

我的第二个解决方案与上面的解决方案类似,涉及QScreen::grabWindow方法和QuickQuickPaintedItem .

MyInterfacePaintedItem.cpp

// Do this for every paint event

// Find the window by it's title
HWND windowHandle = ::FindWindow(0, L"Your Window Title");

auto quickItem = this->targetObject().value<QQuickItem*>();

// can also "throw" the native window into a widget like above and
// use that widgets winId (widget->winId())

auto pixmap = QGuiApplication::primaryScreen()->grabWindow((WId)windowHandle)

painter->drawPixmap(QRect(0, 0, quickItem->width(), quickItem->height()), 
                          pixmap,
                          pixmap.rect());

这个解决方案适用于我传递的每个窗口,除了我想要的窗口 . 我留下了一个空的白色图像而不是所需的图像 .

额外

我尝试将BitBlt与QtWin :: fromHBITMAP(usage)结合使用,但这对我的特定窗口也不起作用 . 我总是得到像上面的非工作解决方案一样的白色图像 . (Potential cause) .

环境

  • Qt:5.10.0

  • 平台:Windows 7

  • 编译器:MSVC2015 32位

1 回答

  • 0

    通过利用DwmRegisterThumbnail,您可以在QML应用程序内的任何位置显示本机OpenGL窗口 . 在我的头顶,这样做的步骤是:

    Adding the DWM dll to QMake

    win32:LIBS += -ldwmapi.dll
    

    Registering the native application's thumbnail with the QML application

    DWM API允许您通过传递源(缩略图的所有者)和目标窗口的窗口句柄(HWND,而不是WId)来向其他应用程序注册应用程序缩略图 .

    注意:目标窗口必须是由您调用此函数的进程创建的顶级窗口!

    DwmRegisterThumbnail((HWND)window()->winId(), hNativeHandle, hThumbnail);
    

    Updating thumbnail size and position

    现在我们已经使用目标窗口注册了缩略图,我们可以利用DwmUpdateThumbnailProperties函数来操纵它在窗口中的大小和位置 . 阅读有关此功能的DWM API文档,并更新缩略图大小和位置,以反映应用程序中所需位置的全局大小和位置 . 我使用了一个放在我的应用程序中的QQuickItem,并将我的缩略图属性基于此 .

相关问题