【问题标题】:GUI freeze when using Windows::SetParent on QApplication在 QApplication 上使用 Windows::SetParent 时 GUI 冻结
【发布时间】:2014-06-09 08:24:25
【问题描述】:

是关于Windows下两个Qt程序的通讯。 一个程序,我们称他为客户端,另一个程序,称为服务器。 情况: 我想将客户端放在服务器的 QWidget 中。 Windows 已经提供了一些很好的方法来删除装饰(边框、标题栏等)并更改窗口的父级,因此重新绘制、调整大小、激活窗口都由 Windows 负责。 当我使用 QProcess 启动我的客户端时,我等待它启动,以便有一个可以与之交谈的窗口。然后我删除装饰并将服务器的 QWidget 设置为父级。 全部用这段代码完成:

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);
 }

这一切都很完美,我的客户很好地放在了我的标签上,所有的重绘等工作都很完美。 但是,我面临的问题是,有时(并非总是如此!)servers' 按钮没有响应。 我注意到当这种情况发生时,按钮不会响应只要它们位于屏幕中间。但是,最奇怪的是,如果我移动整个窗口,使按钮靠近屏幕边缘 - 它们可以工作!如果我将它移回中心 - 他们会再次停止工作。 任何的想法??有人吗?

我还尝试了以下代码:

        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);

使用此解决方案,GUI 不会冻结,但键盘现在无法在客户端窗口(内部窗口)中工作。 例如 - 如果内部窗口是记事本 - 我不能在里面输入,但我可以使用鼠标。 知道可以做什么吗?

【问题讨论】:

  • 跨进程重新设置窗口永远不会结束。您不应该这样做。你几乎没有机会把这项工作做好。你会想找到一个不同的解决方案来解决你的问题。
  • 你有什么建议吗?
  • 在单个进程中运行整个 UI。
  • 使用 Qt 你将没有机会让它工作。曾经。停止做你现在正在做的事情,不要落入它几乎已经起作用了的陷阱。虽然@David 已经提供了建议,但这还不够。如果您的 GUI 跨线程分布,您将在单个进程中遇到相同的问题。换句话说:使您的 GUI 成为单线程的。如果这不是一个选项,您可能需要评估将客户端实现为CLSCTX_LOCAL_SERVER ActiveX 控件,它可以在上下文之外运行。
  • @IInspectable 不够是有点苛刻。大量未列出且无法在评论中列出的要求。你也没有把它们都列出来。

标签: qt winapi qwidget setparent


【解决方案1】:

您可以尝试通过为本机窗口获取QWindow,然后为其创建QWidget 包装器来实现此目的。这至少需要 Qt 5.2。

例如:

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.

您需要向winHandle 询问有关its minimum and maximum size 的信息,并将这些信息传递给widget。如果您打算与之交互,还需要让 widget 成为焦点。

【讨论】:

  • 好的。我正在尝试使用 qt 5.1,但它没有用。切换到 qt 5.2,现在我没有收到那个错误,窗口嵌入到主窗口中,但你只能在左侧看到它的一个非常瘦的部分
  • @one 你如何管理widget 的大小?你使用布局管理器吗?您是否尝试设置最小尺寸?
  • 我添加了一个布局并设置了最小尺寸,现在它看起来很漂亮。但现在的问题是键盘在内部窗口中不起作用。
  • @one 你是否让小部件成为焦点?
  • 是的。我使用了 widget->setFocusPolicy(Qt::StrongFocus);widget->setFocus();小部件->grabKeyboard();所以第一秒钟我有keboard,但是一旦失去焦点,我就无法把它带回来
【解决方案2】:

清除要重新设置父窗口的 WS_POPUP 样式并设置 WS_CHILD 位。否则,我认为 SetParent 实际上建立了所有者/拥有关系,而不是父/子关系。

当您重新定位子级时,它应该位于父级的客户端坐标中。您使用屏幕坐标这一事实很好地暗示了您没有正确的父/子关系。 (这也与在屏幕左上角附近工作的 UI 一致,屏幕坐标将非常接近客户端坐标。)

尽管有负面的 cmets,但有可能使一切都与跨进程父/子窗口一起工作。也就是说,可能要做到这一点很有挑战性。

【讨论】:

    猜你喜欢
    • 2021-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-13
    • 1970-01-01
    • 2018-10-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多