82

Consider this little piece of code:

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_())

The goal here is to fix the code so the window attaching/detaching into a QDockWidget will be made properly. Right now, the code has 2 important issues.

Issue1

Style of the original window is screwed up:

a) Before attaching (the calculator has a menu bar)

enter image description here

b) When attached (the calculator menu bar is gone)

enter image description here

c) When detached (the menu bar hasn't been restored properly)

enter image description here

I've already tried using flags/setFlags qt functions or getWindowLong/setWindowLong but I haven't had luck with all my attempts

Issue2

If you have attached and detached the calculator to the mainwindow, and then you decide to close the mainwindow, you definitely want everything (pyqt process) to be closed and cleaned properly. Right now, that won't be the case, why?

In fact, when you've attached/detached the calculator to the mainwindow, the python process will hold and you'll need to force the termination of the process manually (i.e. ctrl+break conemu, ctrl+c cmd prompt)... which indicates the code is not doing things correctly when parenting/deparenting

Additional notes:

Ahmad MOUSSA
  • 2,729
  • 19
  • 31
BPL
  • 9,632
  • 9
  • 59
  • 117
  • 3
    Lol, somebody ripped off the content of my post to that crappy blogspot site. And he didn't even fix nor answered my question, too bad :P – BPL Mar 17 '19 at 10:15
  • 2
    To be honest, I don't care at all about people scrapping content from other sites, the main problem is usually these scrapping sites don't add any additional value to the original content while they just try to SEO their sites to make some money... Btw, what do you mean with `fix the import area`? The line `from PyQt5.Qt import * # noqa` is not precisely the main problem I was having when created this thread, did you read my questions? Anyway, thanks to report that scrapped article – BPL Mar 17 '19 at 10:36
  • 1
    Have you found the solution sir? If so may I have the code? – Andrie Jul 11 '20 at 08:21
  • 1
    Andrie, hi, unfortunately I had given up with this code at the time of opening the question, that was more than 1 year ago... I recall at that time I'd given a lot of bounties but nobody was able to provide any solution to the problem and I lost interest on it. That said, If you ever find the solution, pls feel free to post it here and I'll review it ;) . As you can see this has become quite an interesting one – BPL Jul 11 '20 at 11:04
  • Yeah. So,this totally doesn't work on Windows 10 with Python 3.7.6. I had to change the FindWindowEx line, but I think that might have made things worse. – Tom Myddeltyn Nov 25 '20 at 21:58
  • That was due to the new "calc" in Windows 10. Changed to notepad.exe and had the same issue now. – Tom Myddeltyn Nov 25 '20 at 22:18

1 Answers1

1

I found part of the issue wrt to closing. So when you are creating the self._window in the attach function and you close the MainWindow, that other window (thread) is sitting around still. So if you add a self._window = None in the __init__ function and add a __del__ function as below, that part is fixed. Still not sure about the lack of menu. I'd also recommend holding onto the subprocess handle with self.__p instead of just letting that go. Include that in the __del__ as well.

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

Probably better yet would be to include a closeEvent

    def closeEvent(self, event):
        print('Closing time')
        self.__p.terminate()
        if self._window is not None:
            print('terminating window')
            self._window.close
Tom Myddeltyn
  • 1,307
  • 1
  • 13
  • 27