我如何检测一个 window 在 PyQt5 中何时遮挡另一个?

How can I detect when one window occludes another in PyQt5?

我正在使用 PyQt5 创建具有多个主 windows 的应用程序。我希望能够允许用户保存和加载 window 尺寸和 window 位置。这很容易,例如 QMainWindow.saveGeometry()QMainWindow.loadGeometry() 或相应的 .saveState().loadState() 变体。这些对于位置和大小非常有用,但如果用户移动或调整一个 window 的大小以使其遮挡另一个,我也想恢复此定位。我不介意编写自己的代码来保存每个 window 的信息,但我看不到任何检测 windows 的相对 Z 顺序的方法。我是否在文档中遗漏了它,或者这是不可能的?

要明白我的意思,试试这个:

from PyQt5.QtWidgets import QApplication, QMainWindow, QPlainTextEdit
from PyQt5.QtCore import QSettings
from PyQt5.QtGui import QCloseEvent

'''
context: Linux Mint 19.3 Tricia x86_64 
         Python 3.9
         PyQt5 5.15.1
'''


class RememberWin(QMainWindow):
    def __init__(self, win_name: str):
        super(RememberWin, self).__init__()
        self.win_name = win_name
        self.setWindowTitle(win_name)
        self.can_close = False

    def restore_window(self) -> bool:
        try:
            settings = QSettings("PyQtExamples", "RememberWinTest")
            self.restoreGeometry(settings.value(f'{self.win_name} Geometry'))
            self.restoreState(settings.value(f'{self.win_name} State'))
            return True
        except:
            return False

    def closeEvent(self, event: QCloseEvent):
        if not self.can_close:
            event.ignore()
        else:
            settings = QSettings("PyQtExamples", "RememberWinTest")
            settings.setValue(f'{self.win_name} Geometry', self.saveGeometry())
            settings.setValue(f'{self.win_name} State', self.saveState())
            QMainWindow.closeEvent(self, event)


class ControlWindow(RememberWin):
    def __init__(self, win_name: str = "ControlWindow"):
        super(ControlWindow, self).__init__(win_name=win_name)
        self.can_close = True

        self.window1 = RememberWin(win_name='WindowOne')
        self.window2 = RememberWin(win_name='WindowTwo')

        self.text = QPlainTextEdit(self)
        s = "Try making Window1 wide enough to cover Window2.\n" \
            "Then close this window (auto closes others).\n" \
            "Re-run the app and you'll notice that Window2\n" \
            "is not on top of Window1 which means that this\n" \
            "info isn't getting saved."

        self.text.setPlainText(s)
        self.setCentralWidget(self.text)

        if not self.restore_window():
            self.setGeometry(100, 390, 512, 100)
        if not self.window1.restore_window():
            self.window1.setGeometry(100, 100, 512, 384)
        if not self.window2.restore_window():
            self.window2.setGeometry(622, 100, 512, 384)

        self.window1.show()
        self.window2.show()

    def closeEvent(self, event: QCloseEvent):
        for win in (self.window1, self.window2):
            win.can_close = True
            win.close()
        super(ControlWindow, self).closeEvent(event)


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)

    window = ControlWindow(win_name='ControlWindow (You can only close this one)')
    window.show()

    sys.exit(app.exec_())

实现您想要实现的目标的最简单方法是跟踪当前获得焦点的小部件,或者更准确地说,跟踪上次获得焦点的小部件的top level window

您可以将焦点 windows 作为列表存储在设置中,为每个 window 使用唯一的 objectName(您已经这样做了,所以您只需要使用setObjectName()),然后通过以正确的顺序显示它们来恢复 window,只要对象名称匹配即可。

class RememberWin(QMainWindow):
    def __init__(self, win_name: str):
        超级(RememberWin,自我)。__init__()
        self.win_name = win_name
        <b>self.setObjectName(win_name)</b>
        self.setWindowTitle(win_name)
        self.can_close = 错误

    # ...


class 控制窗口(记住Win):
    def __init__(self, win_name: str = "ControlWindow"):
        # ...
        self.settings = QSettings("PyQtExamples", "RememberWinTest")
        self.zOrder = []
        QApplication.instance().focusObjectChanged.connect(self.focusChanged)

        window顺序 = self.settings.value('windowOrder', type='QStringList')
        topLevelWindows = QApplication.topLevelWidgets()
        如果window订单:
            对于 window 订单中的 objName:
                在 topLevelWindows 中获胜:
                    如果 win.objectName() == objName:
                        win.show()
        别的:
            自我.window1.show()
            自我.window2.show()

    def focusChanged(自我,对象):
        如果不是 obj 或 obj.window() == self.window():
            return
        如果 obj.window() 在 self.zOrder[:-1] 中:
            self.zOrder.删除(obj.window())
        self.zOrder.append(obj.window())

    def closeEvent(自我,事件:QCloseEvent):
        在 (self.window1, self.window2) 中获胜:
            win.can_close = 正确
            win.close()
        self.settings.setValue('windowOrder',
            [w.window().objectName() for w in self.zOrder])
        超级(ControlWindow,自我).closeEvent(事件)