1

It is about the communication two Qt programs under Windows. One program, let's call him Client, The other program, called Server. The situation: I want to place the Client inside a QWidget of the server. Windows already supplies some nice methods for removing the decoration (border,title bar etc..) and change the parent of a window so repainting, resizing, window activation is all taken care by Windows. When I start my Client using a QProcess I wait for it to be launched, so that there is a window I can talk to. Then I remove the decoration and set the QWidget of the Server as parent. All done with this code:

winHandle = ::FindWindowA(NULL, "My Client");//get clients window id
if(winHandle != NULL)
{
   ::ShowWindow(winHandle, SW_HIDE);

    // Remove the window border
    LONG lStyle = GetWindowLong(winHandle, GWL_STYLE);
    lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU); 
    ::SetWindowLong(winHandle, GWL_STYLE, lStyle);

    ::SetParent(winHandle, (HWND)(ui->widget->effectiveWinId()));//set the server's widget to be the parent of the client win
    ::SetWindowPos(winHandle, HWND_TOP, ui->widget->pos().x(), ui->widget->pos().y(), 0, 0 , SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOSIZE | SWP_ASYNCWINDOWPOS);

     ::UpdateWindow(winHandle);
     ::ShowWindow(winHandle, SW_SHOW);
 }

This all works perfectly, my Client is nicely placed on top of my tab and all the repainting etc. is working perfect. BUT, The problem I'm facing is that sometimes (not always!) the servers' buttons become not responding. I noticed that when such a case happens, the buttons don't respond as long as they are located in the middle of the screen. but, the weirdest thing is that if i move the whole window so the buttons are located close to the edge of the screen - they work! if i move it back to the center - they stop working again. any idea?? someone?

I tried also the following code:

        QWindow * window = QWindow::fromWinId((WId) winHandle);
        QWidget * widget = QWidget::createWindowContainer(window);
        widget->setParent( ui->widget);
        QVBoxLayout *layout = new QVBoxLayout();
        layout->addWidget(widget);     
        ui->widget->setLayout(layout);

with this solution, the GUI didn't freeze but the keyboard doesn't work now in the client's window - in the internal window. for example - if the internal window is notepad - i cannot type inside it but i can use the mouse. any idea what can be done?

one
  • 511
  • 5
  • 17
  • 1
    Reparenting windows across processes never ends well. You are not expected to do this. You have practically no chance of ever making this work well. You'll want to find a different solution to your problem. – David Heffernan Jun 09 '14 at 09:59
  • do you have any suggests? – one Jun 09 '14 at 10:05
  • Run the entire UI out of a single process. – David Heffernan Jun 09 '14 at 10:06
  • Using Qt you will not stand a chance to get this to work. Ever. Stop doing what you are doing right now, and don't fall for the *it almost works already* trap. While @David provided a suggestion already, it is not sufficient. You will run into the same issues in a single process, if your GUI is spread across threads. In other words: Make your GUI singlethreaded. If that is not an option you may want to evaluate implementing the client as a `CLSCTX_LOCAL_SERVER` ActiveX control, that can run out-of-context. – IInspectable Jun 09 '14 at 12:03
  • @IInspectable Not sufficient is a little harsh. Plenty of requirements that are not listed and cannot be listed in a comment. You didn't list them all either. – David Heffernan Jun 09 '14 at 12:06
  • @David The *not sufficient* portion was meant to stress the fact, that the trouble is not caused due to the client and server GUI running in different **processes**. The root cause here is, that they run on different **threads**. Moving them to the same process, without moving them onto the same thread will not do anything to solve the issues. (Just in case you took personal offense: The critique was purely on a technical level.) – IInspectable Jun 09 '14 at 12:21
  • @IInspectable Yes. The move to same thread was implicit in my comment but it's good that you stress it. No offence taken at all. – David Heffernan Jun 09 '14 at 12:36

2 Answers2

0

You may try to do this by obtaining a QWindow for the native window, and then creating a QWidget wrapper for it. This needs at least Qt 5.2.

For example:

HWND winHandle = ::FindWindowA(NULL, "My Client");
if (! winHandle) return;
QWindow * window = QWindow::fromWinId((WId)winHandle);
if (! window) return;
QWidget * widget = QWidget::createWindowContainer(window);
if (! widget) return;
// At this point you can use Qt to change window flags, reparent/embed, etc.

You need to ask the winHandle about its minimum and maximum size and pass those onto the widget. You also need to allow the widget to be focusable if you intend to interact with it.

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • ok. i was trying with qt 5.1, and it didn't work. switched to qt 5.2 and now i'm not getting that error, the window is embedded into the main window but you can see only a very skinny part of it on the left hand side – one Jun 10 '14 at 08:28
  • @one How do you manage `widget`'s size? Do you use a layout manager? Did you try setting a minimal size? – Kuba hasn't forgotten Monica Jun 10 '14 at 20:54
  • i added a layout and set minimum size and now it looks beautiful. but the issue now is that the keyboard doesn't work in the internal window. – one Jun 11 '14 at 06:14
  • @one Have you made the widget focusable? – Kuba hasn't forgotten Monica Jun 11 '14 at 18:54
  • Yes. I used widget->setFocusPolicy(Qt::StrongFocus);widget->setFocus(); widget->grabKeyboard(); so for the first second i had the keboard but once the focus was lost i wasn't able to bring it back – one Jun 12 '14 at 05:44
  • @one The `setFocus` call is unnecessary. `grabKeyboard` makes sense here only if you're implementing a manual focus system - if you wish to have explicit control over the keyboard focus. When embedding a window, you'll definitely want that, but that call cannot be made in a constructor, it must be made when your event processing indicates that the widget should be focused. You can probably invoke `grabKeyboard` in reaction to the widget gaining focus. It's up to you how to detect when the keyboard should be released - generally it's only feasible with the mouse. – Kuba hasn't forgotten Monica Jun 12 '14 at 12:02
0

Clear the WS_POPUP style of the window to be reparented and set the WS_CHILD bit instead. Otherwise, I think SetParent actually sets up an owner/owned relationship rather than a parent/child one.

When you reposition the child, it should be in the parent's client coordinates. The fact that you're using screen coordinates is a good hint that you don't have a proper parent/child relationship. (It's also consistent with the UI working near the top-left of the screen where screen coordinates will be very close to client coordinates.)

Despite the negative comments, it is possible to make everything work with cross-process parent/child windows. That said, it can be challenging to get it right.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175