4

I'd like to embed a Qt Application inside Windows (not the other way around, as many other questions have already been answered). To clarify I have a win32 application which I launch a qt python process; this qt python process must be embedded within the win32 application. How can this be done? In the API for QWindow::fromWinId, it clearly states:

"Creates a local representation of a window created by another process or by using native libraries below Qt...."

"...This can be used, on platforms which support it, to embed a QWindow inside a native window, or to embed a native window inside a QWindow."

And secondly QWidget::createWindowContainer appears to only work for embedding native windows inside Qt (not the way I want it).

I am not sure how I would approach creating a QWidget inside QWindow. From this question, it seems the way would be to create a QQuickView with a QWindow::fromWinId; however, I can't seem to find how to bind a QWidget into a QQuickView.

Currently I am actually setting the parent with ::SetParent but there are weird messaging protocols to deal with there so I'd like to try to refactor this with Qt's approach.

Some basic code written so far (PySide2, but C++ or any other language with Qt bindings is fine):

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_())
Community
  • 1
  • 1
Michael Choi
  • 610
  • 5
  • 22
  • Essentially this is not very practical, as you have found out when you called SetParent. The supported way is for all the UI code to reside in the same process. – David Heffernan Jul 22 '19 at 16:28
  • @DavidHeffernan Yes that is definitely true; however, I'd still like to know if such a technique is possible on the Qt side. A lot of work has already been done for the python side so it would save me a lot of resources for a pressing release that is rapidly approaching. – Michael Choi Jul 22 '19 at 16:32
  • I don't think there's much hope of making this work smoothly – David Heffernan Jul 22 '19 at 16:36
  • @DavidHeffernan While I am no qt expert I was still hoping that it could potentially work because the documentation for `fromWinId` explicitly states it is possible – Michael Choi Jul 22 '19 at 17:16
  • 1
    There are hundreds of questions on here from people struggling to make this work well. You found the same problems that everyone else does. – David Heffernan Jul 22 '19 at 17:29
  • @DavidHeffernan Would you mind linking a couple of them or just one so I can see what problems others have encountered? – Michael Choi Jul 22 '19 at 17:41
  • I'd have to do a websearch. I'm sure you could do it just as well. – David Heffernan Jul 22 '19 at 18:21
  • @DavidHeffernan Ok no worries. I don't mean to sound accusatory but you did mention there were many questions of this same question. I've look and I've found mostly questions pertaining embedding native windows inside Qt, I was hoping to find more insight the other way around. – Michael Choi Jul 22 '19 at 18:34
  • 1
    Qt isn't really so relevant. The issues are at the underlying system level. Adding Qt into the mix only makes the difficulties more severe. – David Heffernan Jul 22 '19 at 18:52
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/196833/discussion-between-michael-choi-and-david-heffernan). – Michael Choi Jul 22 '19 at 20:25

1 Answers1

3

First, you need to create a QWindow from HWND so that Qt can handle it:

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

Then you create your Qt Widget interface:

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

Then you parent the top window of you Qt Widget interface to the native window:

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

However, you cannot use Qt to listen to changes in the HWND window. Quoting Qt documentation:

Note: The resulting QWindow should not be used to manipulate the underlying native window (besides re-parenting), or to observe state changes of the native window. Any support for these kind of operations is incidental, highly platform dependent and untested.

In practice, the signals QWindow::widthChanged() and QWindow::heightChanged() are not emitted.

If you want to listen to events from HWND you have to do it the native Win32 way by using SetWindowsHookEx() or SetWinEventHook().

If you are interested in the resize event you can do in C or 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);

with the following 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;
    }
}
Benjamin T
  • 8,120
  • 20
  • 37
  • Besides resizing (which has been handled), how can I know that the windows event hook will handle other messages I might be overlooking? Additionally, I'd like to delegate these native windows messages to qt, then have qt do the resizing rather than doing it from the native application layer (win32). – Michael Choi Jul 24 '19 at 23:21
  • 1
    @MichaelChoi Just read the documentation about `SetWinEventHook()`, you have a list of events you can watch: https://learn.microsoft.com/fr-fr/windows/win32/winauto/event-constants Plus with `SetWindowsHookEx()` you can hook into the window proc function, so you should be able to get any event or message you may be interested in. – Benjamin T Jul 24 '19 at 23:29
  • @MichaelChoi You cannot "delegate" those messages to Qt, I am not even sure this sentence make sense. What happens is that you have to use Win32 API to get events from the `HWND`. Once you have these events you have to use Qt API to resize your top Qt widget by calling `QWidget::resize()` or `QWidget::setGeometry()`. As I said in my answer, you cannot use Qt to get event from the HWND. And you must use Qt to resize widgets. – Benjamin T Jul 24 '19 at 23:40
  • @benjamin_T I see, apologies for not looking at the documentation first. I see that it is possible hook most events, but manipulation would still be done on the C++ win32 side. Is there a standard Qt way of transforming `DWORD event` into a `QEvent` (such as something like `QEvent::createEventFromNativeWin32(event)`? I've not seen an API call for this and I suspect it is not possible; however, Qt must be translating these events somewhere in their application so I was hoping to leverage the same code here. – Michael Choi Jul 24 '19 at 23:40
  • @Benjamin_T I do see that QWindow states `should not be used ... to observe state changes of the native window`, but to clarify what I am asking: 1) I want to use the `SetWinEventHook`, 2) translate the event to a `QEvent`, 3) then pass that event to my `QApplication`. Of course, I could do this manually but is there a way that Qt specifically does this? – Michael Choi Jul 24 '19 at 23:44
  • @MichaelChoi There is no way you can translate a win32 event into a QEvent. You **might** be able to send a Win32 message to the HWND used internally by Qt using something like PostMessage(). However you need to translate the Win32 event into a Win32 message and maybe adjust some parameters, so to me it seems more straightforward to just call Qt API. Also you should not have to listen to a lot of various event types, Qt already receives its own events (mouse click, key press) without doing nothing. So besides the need for resizing and destroy event (maybe), you should not have anything to do. – Benjamin T Jul 25 '19 at 00:15
  • Thanks a lot, also it worth mentioning that it is important to call label->show() before setParent(), otherwise setParent will crash – David Jul 04 '23 at 13:36