【问题标题】:Qt5 QWidget::create() with Win32 HWND embedding not longer working after port from Qt4Qt5 QWidget::create() 与 Win32 HWND 嵌入在 Qt4 移植后不再工作
【发布时间】:2015-05-01 09:51:25
【问题描述】:

以下代码尝试使用 create 方法将自定义创建的 OpenGL 窗口的本机 Win32 HWND 嵌入到 QWidget 中:

viewer_widget::viewer_widget(
    QWidget* parent,
    const viewer::viewer_attributes&     view_attrib,
    const wm::context::attribute_desc&   ctx_attrib,
    const wm::surface::format_desc&      win_fmt)
  : QWidget(parent)
{
    setMouseTracking(true);
    setFocusPolicy(Qt::StrongFocus);

    setAttribute(Qt::WA_NativeWindow, true);
    setAttribute(Qt::WA_PaintOnScreen, true); // disables qt double buffering (seems X11 only since qt4.5, ...)
    setAttribute(Qt::WA_NoSystemBackground, true);
    setAutoFillBackground(false);

    _viewer = make_shared<viewer>(math::vec2ui(100, 100), parent->winId(), view_attrib, ctx_attrib, win_fmt);

    // ok set the native window as this widgets window...and hold thumbs
    QWidget::create(_viewer->window()->window_handle(), true, true);
}

查看器创建一个原生 Win32(或 X11)窗口,将 QWidget 的父级作为父级。它还创建和初始化 OpenGL 上下文。这样做是为了更好地控制上下文创建和实时时间(我知道 Qt5 在这方面有很大改进)。 QWidget::create() 方法现在获取本机窗口的 HWND 并将其嵌入到当前 QWidget 中,因此事件处理完全通过 Qt 完成。

这在 Qt4 上完美运行(最新使用的是 Qt 4.8.6 x64 on Windows 7/8.1 x64 on Visual Studio 2013)。

现在,当移植到 Qt5 时,根据 Qt5 文档,相同的代码应该仍然可以工作。它需要进行细微的更改来解释 WId 类型的更改。 QWidget::winId() 方法仍然返回小部件的本机 HWND 句柄,我使用 spyxx.exe(Visual Studio 工具)对其进行了验证。

但是,代码不再起作用(在 Visual Studio 2013 上的 Windows 7/8.1 x64 上使用 Qt 5.4.0 x64)。本机窗口只是没有嵌入。在 Qt4 中,当检查创建的 QWidget 时,它在创建调用后的本机句柄 (winId) 与本机 HWND 相同,这意味着嵌入工作。现在使用 Qt5,QWidget 包含自己的 HWND,我可以使用 spyxx.exe 再次确认。现在有父窗口小部件/窗口和两个子窗口小部件/窗口,其中应该只有一个孩子(本机)。我查看了两个 Qt 版本的 create() 方法的源代码,但我不明白为什么它不再工作了。

好的,在第一个晚上试图解决这个问题后,我尝试了其他几种我可以在论坛或文档中找到的方法:

  • QWindow::fromWinId(HWND) 和 QWidget::createWindowContainer(QWindow):这似乎是 Qt-Devs 希望我这样做的方式:
_viewer = make_shared<viewer>(math::vec2ui(100, 100), par_hndl, view_attrib, ctx_attrib, win_fmt);
QWindow* native_wnd  = QWindow::fromWinId((WId)_viewer->window()->window_handle());
QWidget* native_wdgt = QWidget::createWindowContainer(native_wnd);

QHBoxLayout* lo        = new QHBoxLayout(this);
lo->setContentsMargins(0, 0, 0, 0);
lo->addWidget(native_wdgt);

至少这至少表现得几乎符合我的预期。我看到了我的窗口,并且在新创建的小部件中嵌入了本机窗口。但是:我发现 no 方法可以从新创建的小部件中获取任何鼠标事件/输入。我添加了一个 eventFilter 没有运气。我尝试了一个带有透明顶级小部件的 QStackedLayout 来捕获输入,但这不起作用,因为通过 createWindowContainer() 创建的小部件 always 在 windows 堆栈的顶部。我尝试创建一个 QWindow 派生类来拦截 nativeEvent() 调用,但没有运气......

我的想法已经结束,无法像 Qt4 一样工作。我有什么办法可以改变旧的行为吗?我可以使用 Qt::StrongFocus 策略通过父小部件跟踪键盘输入,但鼠标输入完全被本机窗口吞没。我无法移动 Qt5 OpenGL 窗口代码,需要使用我们的自定义 OpenGL 上下文工具,因为我们正在使用 Qt5 仍然不完全支持的上下文。

我还不能在 Linux 上试用 Qt5,但上面看到的 Qt4 代码确实在那里工作。

【问题讨论】:

    标签: c++ windows qt opengl


    【解决方案1】:

    我通过子类化QWidget 在很大程度上解决了从Qt 到窗口的通信问题,像您最初所做的那样使用create(),并重新实现QWidget 事件函数以直接更改本机窗口。到目前为止,我发现的(大的)是焦点事件,resizeEvent、moveEvent,以及内容 rect 和启用changeEvents。字体、调色板、工具提示等changeEvents 的优先级可能较低。

    以上并不能解决相反的问题,即来自本机窗口的消息永远不会到达 Qt 的事件调度程序。您需要将消息从您的WndProc 发布到widget 的 HWND(即调用winId() 的返回)。这也几乎绕过了尝试将虚拟键代码转换为 Qt::Keys 并返回的步骤。

    关于为什么这不能像在 Qt4 中那样正常工作的一些说明:

    其他说明:

    • QWidget::effectiveWinId() 是首选,即使您正在创建直接子窗口。不幸的是,这在 5.4.1 中中断。
    • 更新到 5.4.1。 5.4.0 和所有其他版本的 Qt5 存在二进制兼容性问题。

    【讨论】:

    • 对于不完整的答案,您最好使用 cmets 来回答问题。
    • 很抱歉。经过一些研究后修复。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多