将外部应用程序附加到QDockWidget时出现的问题和解除附加的问题

82

考虑这段小代码:

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) 在附加之前(计算器有一个菜单栏)

enter image description here

b) 当附加时(计算器菜单栏消失)

enter image description here

c) 当分离时(菜单栏未正确还原)

enter image description here

我已经尝试使用flags/setFlags qt函数或getWindowLong/setWindowLong,但我在所有尝试中都没有成功

问题2

如果您已经将计算器附加并分离到主窗口,然后决定关闭主窗口,您肯定希望一切(pyqt进程)被正确关闭和清理。现在,这不是这种情况,为什么?

事实上,当您将计算器附加/分离到主窗口时,Python进程将被占用,您需要手动强制终止进程(即ctrl+break conemu,ctrl+c cmd prompt)...这表明代码在父/取消父级关系时没有做正确的事情。

附加注释:


3
有人抄袭了我帖子的内容放到一个糟糕的博客站点上。他甚至没有修复也没有回答我的问题,太糟糕了:P - BPL
2
说实话,我完全不关心人们从其他网站上爬取内容的事情,主要问题通常是这些抓取网站没有为原始内容添加任何附加值,而只是试图通过 SEO 来赚取一些钱… 顺便问一下,你说的“修复导入区域”是什么意思?这行代码from PyQt5.Qt import * # noqa并不是我在创建这个帖子时遇到的主要问题,你有看过我的问题吗?无论如何,感谢你汇报了那篇被抓取的文章。 - BPL
1
先生,您找到解决方案了吗?如果找到了,能否给我提供代码? - Andrie
1
Andrie,你好,不幸的是,在我提出这个问题的时候,我已经放弃了这段代码,那是一年多以前的事情了... 我记得当时我已经给了很多赏金,但是没有人能够提供任何解决方案,我对此失去了兴趣。话虽如此,如果你找到了解决方案,请随时在这里发布,我会进行审查;)正如你所看到的,这已经变成了一个非常有趣的问题。 - BPL
是的。所以,在Windows 10上使用Python 3.7.6完全不起作用。我不得不更改FindWindowEx行,但我认为这可能会使情况变得更糟。 - Tom Myddeltyn
那是由于Windows 10中的新“calc”。更改为notepad.exe,现在出现了相同的问题。 - Tom Myddeltyn
1个回答

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接