创建 QWidget 宽度 HWND 父级

Posted

技术标签:

【中文标题】创建 QWidget 宽度 HWND 父级【英文标题】:create QWidget width HWND parent 【发布时间】:2018-04-05 06:24:37 【问题描述】:

操作系统窗口。 Qt 5.5.1 我在 Qt 上用 GUI 制作了库(dll)。 我将它连接到新项目。我有父母的hwnd。如何为库窗口(Qwidget)设置父级? 如果使用winapi SetParent(),则子窗口不离开父窗口。 我尝试了 QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow),但它不起作用,因为根据文档,Qt 5 中忽略了参数 window。

【问题讨论】:

【参考方案1】:

我找到了一种可行的方法,但是屏幕上出现了未知消息

windowsProc: No Qt Window found for event 0x24 (WM_GETMINMAXINFO), hwnd=0x0x110956.
windowsProc: No Qt Window found for event 0x83 (WM_NCCALCSIZE), hwnd=0x0x110956.
windowsProc: No Qt Window found for event 0x5 (WM_SIZE), hwnd=0x0x110956.
windowsProc: No Qt Window found for event 0x3 (WM_MOVE), hwnd=0x0x110956.
setGeometryDp: Unable to set geometry 640x480+0+0 on QWindow/''. Resulting geometry:  640x480+-760+-370 (frame: 8, 30, 8, 8, custom margin: 0, 0, 0, 0, minimum size: 0x0, maximum size: 16777215x16777215).
setGeometryDp: Unable to set geometry 640x480+0+0 on QWindow/''. Resulting geometry:  640x480+-760+-370 (frame: 8, 30, 8, 8, custom margin: 0, 0, 0, 0, minimum size: 0x0, maximum size: 16777215x16777215).
listLabels.size() 4

设父窗口为ownerWin,子窗口为名为childWin的QWidget-window;

如果使用SetWindowLongPtr,可以达到理想的效果(子窗口与父窗口重叠),但是当父窗口关闭时,子窗口还在内存中。

为了解决一个问题,我使用 WinApi 再创建了一个子窗口(middleWin 和父 ownerWin。)。使用 createWindowContainer() 用 QWidget 包装此窗口后,它会停止正常工作。然后使用标准的 Qt 方法,我们可以链接我们的 QWidget 窗口。所以我们有三个窗口:父窗口、中间窗口和我的窗口。然后我使用 close() 关闭 middleWin。所有这些操作都需要确保当 ownerWin 关闭时,childWin 也会关闭。如果使用 SetWindowLongPtr,则 childWin 将始终位于父窗口之上,但只有在子窗口中的函数 show() 之后调用时才有效。

以下是我使用的功能。首先调用 setWinParent(),然后调用 showWindow()。 在 setWinParent() 行 1 到 5 对我来说是“黑匣子”。我不知道为什么会这样创建窗口。

void setWinParent(HWND ownerWin)

    HWND hwnd = (HWND)this->winId();
    DWORD exStyle = GetWindowLong(hwnd, GWL_EXSTYLE) ;//1
    DWORD style   = GetWindowLong(hwnd, GWL_STYLE);//2
    WCHAR className[256];//3
    GetClassName(hwnd, className,256);//4
    HWND newHwnd = CreateWindowEx(exStyle, className, NULL, style,//5
                                  CW_USEDEFAULT, CW_USEDEFAULT,
                                  CW_USEDEFAULT, CW_USEDEFAULT,
                                  ownerWin, NULL, qWinAppInst(), NULL);
    QWindow *qw=QWindow::fromWinId((WId)newHwnd);
    QWidget* qWidget = createWindowContainer(qw);
    qWidget->show();
    this->setParent(qWidget);
    this->setWindowFlags(Qt::Window);
    qWidget->close();    

void showWindow(HWND ownerHwnd)

    show();
    SetWindowLongPtr((HWND)this->winId(), GWLP_HWNDPARENT, (LONG)ownerHwnd);

【讨论】:

【参考方案2】:

不幸的是,这是 Qt 在 Windows(和其他平台)上长期存在的问题。您必须找到一种方法来挂钩到父窗口以捕获鼠标和键盘事件等以传播到您的 Qt 子窗口。用于此的内部 Qt 代码一直在进行中。

有一个关于这些问题和一般主题的好页面here。这里也是讨论这个问题的bug report。

注意:QAxWidget 也不是很有帮助。它看起来很有希望,但在事件传播方面遇到了同样的问题。

【讨论】:

在 Qt5 中将是 nativeEvent。问题是关于重新育儿,而不是事件处理,但显然 OP 解决了 xy 问题 理想情况下,是的,nativeEvent 可以工作。不幸的是,事实并非如此。如果父(本机)窗口不知道子(Qt)窗口支持它们,您就不能告诉父(本机)窗口发送焦点、键盘和鼠标事件。 很好,他没有引用事件,但 Qt 不能正确处理本机窗口是更大问题的一部分。你可以它,但它没有集成。 它只是没有完全使用它们,除了从 API 接收那些。这允许在不支持任何窗口系统的情况下重新编译 Qt:例如使用 linux 内核帧缓冲区直接渲染到屏幕 - 不需要 X11 或 Wayland【参考方案3】:

Qt 5 中忽略了参数窗口。请使用 QWindow::fromWinId() 创建一个包装外部窗口的 QWindow 和 而是将其传递给 QWidget::createWindowContainer()。

Qt 文档有什么不清楚的地方?

QWindow *wndParent = QWindow::fromWinId(hwnd); // hwnd - your WId
if(wndParent) 

    QWidget *parent = QWidget::createWindowContainer(wndParent);
    if(parent)
        // ...

else

    // unsupported window...

【讨论】:

我的窗口已经是QWidget了。如果我为父窗口制作包装,那么我可以 myWindow->setParent(wrapped parent)。但是父窗口坏了:没有重绘,没有鼠标事件,没有调整大小 也来自文档:“注意:生成的 QWindow 不应用于操作底层的原生窗口(除了重新设置),或观察原生窗口的状态变化。对这些的任何支持这类操作是偶然的、高度依赖平台且未经测试的。” Qt 不能以这种方式正确支持 Windows。 @Владимир 你必须为那些使用原始小部件。如果您已经有了 Qt 对象,为什么还需要 hwd 来为您的窗口设置父级? Qt 框架不是 API 的包装器,它本身是 GUI 的抽象,它以自己的方式跟踪和存储状态并处理事件。所有 winodows api 或 x11 小部件都是“windows”/“widgets”。 Qt 小部件可能只是 API 中不存在的渲染对象(非原生小部件)。 我的 *.dll 有 GUI。主程序将在没有 QT 的 MSVS 上。我希望当主程序关闭时我的 dll 窗口会自动关闭,而且我的窗口必须在主程序窗口的上方。因此,my-dll-window 的父级必须是主程序,但不是 QWidget。

以上是关于创建 QWidget 宽度 HWND 父级的主要内容,如果未能解决你的问题,请参考以下文章

QWidget 如何获得“整个”父级?

由父级绘制 QWidget

强制 QObject 作为 QWidget 的父级有啥后果?

QWidget 失去了它的父级

QT 指针和对象 句柄转换

Qt 计算器界面实现