从 QDockWidget 附加和分离外部应用程序时的问题

Posted

技术标签:

【中文标题】从 QDockWidget 附加和分离外部应用程序时的问题【英文标题】:Issues when attaching and detaching external app from QDockWidget 【发布时间】:2019-06-20 16:05:01 【问题描述】:

考虑一下这段小代码:

import subprocess
import win32gui
import win32con
import time
import sys
from PyQt5.Qt import *  # noqa


class Mcve(QMainWindow):

    def __init__(self, path_exe):
        super().__init__()

        menu = self.menuBar()

        attach_action = QAction('Attach', self)
        attach_action.triggered.connect(self.attach)
        menu.addAction(attach_action)

        detach_action = QAction('Detach', self)
        detach_action.triggered.connect(self.detach)
        menu.addAction(detach_action)

        self.dock = QDockWidget("Attach window", self)
        self.addDockWidget(Qt.RightDockWidgetArea, self.dock)

        p = subprocess.Popen(path_exe)
        time.sleep(0.5)  # Give enough time so FindWindowEx won't return 0
        self.hwnd = win32gui.FindWindowEx(0, 0, "CalcFrame", None)
        if self.hwnd == 0:
            raise Exception("Process not found")

    def detach(self):
        try:
            self._window.setParent(None)
            # win32gui.SetWindowLong(self.hwnd, win32con.GWL_EXSTYLE, self._style)
            self._window.show()
            self.dock.setWidget(None)
            self._widget = None
            self._window = None
        except Exception as e:
            import traceback
            traceback.print_exc()

    def attach(self):
        # self._style = win32gui.GetWindowLong(self.hwnd, win32con.GWL_EXSTYLE)
        self._window = QWindow.fromWinId(self.hwnd)
        self._widget = self.createWindowContainer(self._window)
        self.dock.setWidget(self._widget)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Mcve("C:\\Windows\\system32\\calc.exe")
    w.show()
    sys.exit(app.exec_())

这里的目标是修复代码,以便正确地将窗口附加/分离到 QDockWidget 中。目前,代码有两个重要问题。

问题1

原来窗口的样式搞砸了:

a) 附加前(计算器有菜单栏)

b) 连接时(计算器菜单栏消失)

c) 分离时(菜单栏未正确恢复)

我已经尝试过使用 flags/setFlags qt 函数或 getWindowLong/setWindowLong 但我的所有尝试都没有运气

问题2

如果您已将计算器附加和分离到主窗口,然后您决定关闭主窗口,那么您肯定希望正确关闭和清理所有内容(pyqt 进程)。现在,不会是这样,为什么?

事实上,当您将计算器附加/分离到主窗口时,python 进程将保持,您需要手动强制终止进程(即 ctrl+break conemu,ctrl+c cmd 提示符) ...这表明代码在养育/去亲时没有正确处理

补充说明:

http://doc.qt.io/qt-5/qwindow.html#fromWinId http://doc.qt.io/qt-5/qwidget.html#createWindowContainer 在上面的最小代码中,我将 calc.exe 作为子进程生成,但您可以假设 calc.exe 是由 explorer.exe 生成的现有非子进程

【问题讨论】:

大声笑,有人将我的帖子内容抄袭到那个糟糕的 blogspot 网站。而且他甚至没有解决或回答我的问题,太糟糕了:P 说实话,我根本不在乎人们从其他网站上抓取内容,主要问题通常是这些抓取网站在尝试时不会为原始内容增加任何附加值对他们的网站进行 SEO 以赚钱...顺便说一句,fix the import area 是什么意思? from PyQt5.Qt import * # noqa 这行并不是我在创建这个线程时遇到的主要问题,你读过我的问题吗?无论如何,感谢报告那篇报废的文章 您找到解决方案了吗?如果可以,我可以给我代码吗? 安德烈,嗨,不幸的是,我在打开问题时已经放弃了这段代码,那是一年多以前......我记得当时我给了很多赏金,但没有人能够为这个问题提供任何解决方案,我对此失去了兴趣。也就是说,如果您找到解决方案,请随时在此处发布,我会对其进行审查;)。正如你所看到的,这已经变得非常有趣了 是的。因此,这完全不适用于带有 Python 3.7.6 的 Windows 10。我不得不更改 FindWindowEx 行,但我认为这可能会让事情变得更糟。 【参考方案1】:

我发现部分问题要关闭。因此,当您在attach 函数中创建self._window 并关闭MainWindow 时,其他窗口(线程)仍处于静止状态。因此,如果您在__init__ 函数中添加self._window = None 并添加如下__del__ 函数,则该部分是固定的。仍然不确定是否缺少菜单。我还建议使用self.__p 握住子进程句柄,而不是放手。也将其包含在 __del__ 中。

    def __del__(self):
        self.__p.terminate()
        if self._window:
            print('terminating window')
            self._window.close

最好包含closeEvent

    def closeEvent(self, event):
        print('Closing time')
        self.__p.terminate()
        if self._window is not None:
            print('terminating window')
            self._window.close

【讨论】:

以上是关于从 QDockWidget 附加和分离外部应用程序时的问题的主要内容,如果未能解决你的问题,请参考以下文章

QDockWidget - QGlWidget - 当小部件从停靠栏中拖出时,可绘制无效

使用从元素 id 的变量构建的数组分离和附加复选框选择

片段事务分离和附加后ListView不工作?

Laravel 根据枢轴额外字段从枢轴附加/分离模型

分离和附加数据库

sqlserver 分离和附加