【发布时间】:2018-11-04 01:00:49
【问题描述】:
问题
我需要一个在 QML 应用程序中显示的 Windows 本机窗口。
约束
- 本机窗口 (NW) 应用程序是使用 OpenGL 绘制的
- NW 仅用于显示目的,不需要用户输入
- 不允许 3rd 方库
- 您可以访问 NW 的源代码
- 注意:将此特定源代码移植到 QOpenGLWidget 是不被接受的
工作解决方案
一个可行的解决方案是利用QWindow::fromWinId 和QWidget::createWindowContainer 方法将窗口“扔”到QWidget 中。然后我们从 QML 中获取一个项目并强制这个小部件“镜像”该项目的尺寸和位置。
以下代码 sn-ps 演示了一种可能的实现方式。
(注意:这些 sn-ps 被改写并去掉了错误检查)
main.qml
id 为“target”的 Rectangle 被传递到 MyInterfaceObject 的 targetObject 属性中。
Rectangle {
id: app
.
.
Rectangle {
id: target
MyInterfaceObject {
targetObject: target
.
.
}
}
MyInterfaceObject.cpp
MyInterfaceObject 是一个具有 QVariant targetObject 属性的 QObject。它需要一个所需的窗口句柄并将其“扔”到一个小部件上。然后,它启动一个计时器,将嵌入的小部件映射到目标对象的位置。
可能会根据一些 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 32bit
【问题讨论】:
-
本机 OpenGL 不会像这样绘制到您可以使用 Qt 抓取的表面,这就是您获得背景颜色矩形的原因。即使使用 QOpenGLWidget 也无法使用,除非您将其植入 QGraphicsScene 中。您必须使用屏幕外渲染和渲染图像。请注意,QML 在默认配置中固有地使用 Windows 上的 OpenGL 来呈现内容。