我如何检测一个 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(事件)
我正在使用 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(事件)