从Qt4移植后,带有Win32 HWND嵌入的Qt5 QWidget :: create()不再起作用 [英] Qt5 QWidget::create() with Win32 HWND embedding not longer working after port from Qt4

查看:135
本文介绍了从Qt4移植后,带有Win32 HWND嵌入的Qt5 QWidget :: create()不再起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

the following code tries to embed a native Win32 HWND of a custom created OpenGL windows into a QWidget using the create method:

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完成事件处理.

The viewer creates a native Win32 (or X11) window wit the parent of the QWidget as a parent. It also creates and initializes an OpenGL context. This was/is done for more control over the context creation and live time (I know that Qt5 is much improved in that regard). The QWidget::create() method now takes the HWND of the native window and embeds it into the current QWidget, so that event handling is completely done through Qt.

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

This works perfectly on Qt4 (latest used is 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工具)对其进行了验证.

Now when porting to Qt5 the same code should still work according to the Qt5 documentation. It required minor changes to account for the change in the WId type. The QWidget::winId() methods still return the native HWND handles of the widgets, which I verified using spyxx.exe (Visual Studio Tools).

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

However, the code does not work anymore (using Qt 5.4.0 x64 on Windows 7/8.1 x64 on Visual Studio 2013). The native window is just not embedded. In Qt4 when inspecting the created QWidget its native handle (winId) after the create call was identical to the native HWND, which meant the embedding worked. Now using Qt5 the QWidget contains its own HWND which I could again confirm using spyxx.exe. Now there is the parent widget/window and two child widgets/windows where there should only be one child (the native one). I looked at the source code of the create() method for both Qt versions and I do not understand why it is not working anymore.

好吧,在第一天晚上想弄清楚这一点之后,我尝试了在论坛或文档中可以找到的其他几种方法:

Ok, after the first night trying to figure this out I tried several other methods I could find on forums or the documentation:

  • 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()创建的窗口小部件总是 在Windows堆栈顶部,因此该方法不起作用.我尝试创建一个QWindow派生的类来拦截nativeEvent()调用,但是没有运气...

This at least at least behaves almost the way I expect. I see my window and in the newly created widget the native window is embedded. BUT: I have found no way to get any mouse events/input from that newly created widget. I added an eventFilter without luck. I tried a QStackedLayout with a transparent top-level widget to catch the input, but this does not work as the widget created through createWindowContainer() is always on top of the windows stack. I tries creating a QWindow-derived class to intercept the nativeEvent() invocation, without luck...

我想尽一切办法使Qt4能够正常工作.我有什么可以改变的方式来恢复旧的行为?我可以使用Qt :: StrongFocus策略在父窗口小部件中跟踪键盘输入,但是鼠标输入会被本机窗口完全吞噬.我无法移动Qt5 OpenGL窗口代码,需要使用我们的自定义OpenGL上下文工具,因为我们正在处理Qt5仍不完全支持的上下文.

I am at the end of my ideas to get this working as with Qt4. Is there anything I can to differently to get the old behavior back? The keyboard input I can track through the parent widget using a Qt::StrongFocus policy but the mouse input in complete swallows by the native window. I cannot move the the Qt5 OpenGL window code and need to use our custom OpenGL context tools as we are doing things with the contexts that Qt5 still does not fully support.

我还不能在Linux上尝试Qt5,但是上面看到的Qt4代码确实可以在这里工作.

I could not yet try Qt5 on Linux, but the Qt4 code as seen above did work there.

推荐答案

我已经解决了从Qt到窗口的通信问题,方法是通过继承QWidget的子类,像您最初使用的那样使用create()并重新实现QWidget事件函数直接更改本机窗口.到目前为止,我发现的那些(大的)是焦点事件resizeEventmoveEvent和内容rect并启用changeEvent.字体,调色板,工具提示等.changeEvents的优先级可能较低.

I have largely solved communication from Qt to the window by subclassing QWidget, using create() as you originally did, and reimplementing the QWidget event functions to make changes to the native window directly. The ones I've found so far (the big ones) are the focus events, resizeEvent, moveEvent, and the contents rect and enable changeEvents. Font, palette, tooltip, etc. changeEvents are probably a lower priority.

以上内容无法解决相反的问题,即来自本机窗口的消息永远不会到达Qt的事件分派器中.您需要将消息从您的WndProc发送到小部件的 HWND(即,调用winId()的返回).这也几乎绕过尝试将虚拟键代码转换为Qt::Key s并返回的过程.

The above won't solve the reverse issue, that messages from the native window never arrive in Qt's event dispatcher. You will need to post messages from your WndProc to the widget's HWND (i.e., the return from calling winId()). This also nearly steps around trying to translate virtual key codes to Qt::Keys and back.

一些说明,为什么它不能像Qt4那样正常运行:

Some notes about why this doesn't work gracefully, as it did in Qt4:

  • QWindow only works on X11 with native child widgets right now. Try testing on Linux as you mentioned.
  • QWidget::create() and QWindow have been problems since 5.1 at the least. The problems you're having in both implementations seem to fit.
  • Here is a consolidated bug report about QWindow intended usage and issues

其他说明:

    即使您要创建直接子窗口,也最好使用
  • QWidget::effectiveWinId().不幸的是,这打破了5.4.1.
  • 更新至5.4.1. 5.4.0和Qt5的所有其他版本都存在二进制兼容性问题.
  • QWidget::effectiveWinId() is preferred even though you are creating an immediate child window. This, unfortunately, breaks in 5.4.1.
  • Update to 5.4.1. There are binary compatibility issues with 5.4.0 and all other versions of Qt5.

这篇关于从Qt4移植后,带有Win32 HWND嵌入的Qt5 QWidget :: create()不再起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆