【问题标题】:Embed Qt inside native window (Windows)将 Qt 嵌入本机窗口 (Windows)
【发布时间】:2019-11-30 15:20:25
【问题描述】:

我想在 Windows 中嵌入一个 Qt 应用程序(不是相反,因为许多其他问题已经得到解答)。为了澄清我有一个 win32 应用程序,我启动了一个 qt python 进程;这个 qt python 进程必须嵌入到 win32 应用程序中。如何才能做到这一点?在 QWindow::fromWinId 的 API 中,它明确指出:

“创建窗口的本地表示由另一个进程创建或使用 Qt 下面的本机库......”

"...这可以在支持它的平台上使用,将 QWindow 嵌入到本地窗口中,或将本地窗口嵌入到 QWindow 中。"

其次,QWidget::createWindowContainer 似乎只适用于在 Qt 中嵌入原生窗口(不是我想要的方式)。

我不确定如何在QWindow 中创建QWidget。从这个question 看来,方法似乎是用QWindow::fromWinId 创建一个QQuickView;但是,我似乎找不到如何将 QWidget 绑定到 QQuickView 中。

目前我实际上使用::SetParent 设置父级,但是那里有奇怪的消息传递协议要处理,所以我想尝试用 Qt 的方法重构它。

到目前为止编写的一些基本代码(PySide2,但 C++ 或任何其他带有 Qt 绑定的语言都可以):

app = QApplication(sys.argv)
hwnd = int(sys.argv[1], 16)

nativeParentWindow = QWindow.fromWinId(hwnd)
quickview = QQuickView(nativeParentWindow)

# this part is incorrect (tries to embed native window into qt)
# I want this application to run embedded inside hwnd
wrongWidget = QWidget.createWindowContainer(quickview)
wrongWidget.show()

sys.exit(app.exec_())

【问题讨论】:

  • 基本上这不是很实用,正如您在调用 SetParent 时发现的那样。支持的方式是将所有 UI 代码驻留在同一进程中。
  • @DavidHeffernan 是的,这绝对是真的;但是,我仍然想知道这种技术在 Qt 方面是否可行。 Python 方面已经完成了很多工作,因此它可以为我节省大量资源,以便快速发布即将发布的新闻稿。
  • 我认为让这项工作顺利进行的希望不大
  • 这里有数百个问题来自人们努力使这项工作顺利进行。你发现了和其他人一样的问题。
  • Qt 并不是那么重要。问题在于底层系统级别。将 Qt 添加到混合中只会使困难更加严重。

标签: python c++ qt winapi pyside2


【解决方案1】:

首先,您需要从 HWND 创建一个 QWindow 以便 Qt 可以处理它:

// C++
QWindow *nativeWindow = QWindow::fromWinId(hwnd);
// Python
nativeWindow = QWindow.fromWinId(hwnd);

然后你创建你的 Qt Widget 界面:

// C++
QLabel *label = new QLabel("Hello from Qt");
label->show();
// Python
label = QLabel("Hello from Qt");
label.show();

然后你将 Qt Widget 界面的顶部窗口作为原生窗口的父窗口:

// C++
label->windowHandle()->setParent(nativeWindow);
// Python
label.windowHandle().setParent(nativeWindow);

但是,您不能使用 Qt 来监听 HWND 窗口中的变化。引用Qt documentation:

注意:生成的 QWindow 不应用于操作 底层原生窗口(除了重新设置),或观察状态 本机窗口的变化。对此类操作的任何支持 是偶然的,高度依赖于平台且未经测试。

实际上,QWindow::widthChanged()QWindow::heightChanged() 信号不会发出。

如果您想监听来自 HWND 的事件,您必须使用本机 Win32 方式,使用 SetWindowsHookEx()SetWinEventHook()

如果您对可以在 C 或 C++ 中执行的调整大小事件感兴趣:

targetHwnd = hwnd; // defined on global scope
DWORD processId;
DWORD threadId= GetWindowThreadProcessId(hwnd, &processId);
HWINEVENTHOOK hook = SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, 0, &Wineventproc, processId, threadId, WINEVENT_OUTOFCONTEXT);

使用以下Wineventproc

void WINAPI Wineventproc(
  HWINEVENTHOOK hWinEventHook,
  DWORD event,
  HWND hwnd,
  LONG idObject,
  LONG idChild,
  DWORD idEventThread,
  DWORD dwmsEventTime
)
{
    if(targetHwnd == hwnd) { // Filter events that are not for our window
        qDebug() << "event" << event;
        RECT rect;
        GetWindowRect(hwnd, &rect);
        qDebug() <<  "Height:" << rect.bottom - rect.top;
        qDebug() <<  "Width:" << rect.right - rect.left;
    }
}

【讨论】:

  • 除了调整大小(已处理)之外,我怎么知道 Windows 事件挂钩会处理我可能忽略的其他消息?此外,我想将这些本机 Windows 消息委托给 qt,然后让 qt 进行大小调整,而不是从本机应用程序层 (win32) 进行调整。
  • @MichaelChoi 只需阅读有关SetWinEventHook() 的文档,您就有可以观看的事件列表:docs.microsoft.com/fr-fr/windows/win32/winauto/event-constants 加上SetWindowsHookEx(),您可以挂钩到窗口 proc 函数,所以您应该能够获得您可能感兴趣的任何事件或消息。
  • @MichaelChoi 您不能将这些消息“委托”给 Qt,我什至不确定这句话是否有意义。发生的情况是您必须使用 Win32 API 从HWND 获取事件。一旦你有了这些事件,你必须使用 Qt API 通过调用QWidget::resize()QWidget::setGeometry() 来调整你的顶级 Qt 小部件的大小。正如我在回答中所说,您不能使用 Qt 从 HWND 获取事件。而且你必须使用 Qt 来调整小部件的大小。
  • @benjamin_T 我明白了,很抱歉没有先看文档。我看到它可能会钩住大多数事件,但仍然可以在 C++ win32 端进行操作。是否有将DWORD event 转换为QEvent 的标准Qt 方法(例如QEvent::createEventFromNativeWin32(event)?我没有看到对此的API 调用,我怀疑这是不可能的;但是,Qt 必须翻译这些他们应用程序中某处的事件,所以我希望在这里利用相同的代码。
  • @Benjamin_T 我确实看到 QWindow 声明 should not be used ... to observe state changes of the native window,但要澄清我的要求:1)我想使用 SetWinEventHook,2)将事件转换为 QEvent, 3) 然后将该事件传递给我的QApplication。当然,我可以手动执行此操作,但 Qt 有没有专门执行此操作的方法?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多