parentWidget 关闭时浮动 QDockWidget 不会关闭 [英] floating QDockWidget does not close when parentWidget closes
问题描述
我有两个打开的 MainWindows:MainWindowWithButton 和 MainWindowWithDock.后者包含一个 QDockWidget.
I have two open MainWindows: MainWindowWithButton and MainWindowWithDock. The later contains a QDockWidget.
IS 行为:当用户使 DockWidget 可浮动并关闭 MainWindowWithDock 时,dockWidget 不会关闭.
IS behaviour: When the users makes the DockWidget floatable and closes MainWindowWithDock, the dockWidget doesn't close.
应该的行为:当用户使 DockWidget 可浮动并关闭 MainWindowWithDock 时,dockWidget 也会关闭.
SHOULD behaviour: When the users makes the DockWidget floatable and closes MainWindowWithDock, the dockWidget closes as well.
注意事项:
- IS 行为"的原因:浮动 DockWidget 似乎独立于它的父级
- 我无法监听 onClose/reject(因为它会在我的特定情况下提供虚假信息.
- MainWindow 不会发出有关其行为的明确信号
- 重要的是,DockWidget 在 MainWindow 关闭之前关闭.否则焦点会出乎意料
- Reason for "IS behaviour": A floating DockWidget seems to be independent from it's parent
- I cannot listen for onClose / reject (as it will give false information in my particular case.
- The MainWindow does not emit clear signals about it's behaviour
- It is important, that the DockWidget closes before the MainWindow closed. Otherwise the focus goes unexpected
示例代码:
from PyQt4 import QtCore, QtGui
from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import QApplication, QDialog, QMainWindow
import sys
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_MainWindowWithButton(object):
def setupUi(self, MainWindowWithButton):
MainWindowWithButton.setObjectName(_fromUtf8("MainWindowWithButton"))
MainWindowWithButton.resize(567, 384)
self.centralwidget = QtGui.QWidget(MainWindowWithButton)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
MainWindowWithButton.setCentralWidget(self.centralwidget)
def retranslateUi(self, MainWindowWithButton):
MainWindowWithButton.setWindowTitle(_translate("MainWindowWithButton", "MainWindow", None))
class Ui_MainWindowWithDock(object):
def setupUi(self, MainWindowWithDock):
MainWindowWithDock.setObjectName(_fromUtf8("MainWindowWithDock"))
MainWindowWithDock.resize(509, 316)
self.centralwidget = QtGui.QWidget(MainWindowWithDock)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
MainWindowWithDock.setCentralWidget(self.centralwidget)
# # # # # # # # # # # # # # # # # # # # # #
# # # setup dock # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # #
self.theDock = QtGui.QDockWidget(MainWindowWithDock)
self.theDock.setObjectName(_fromUtf8("theDock"))
self.dockWidgetContents = QtGui.QWidget(self.theDock)
self.dockWidgetContents.setObjectName(_fromUtf8("dockWidgetContents"))
self.theDock.setWidget(self.dockWidgetContents)
MainWindowWithDock.addDockWidget(QtCore.Qt.DockWidgetArea(2), self.theDock)
self.retranslateUi(MainWindowWithDock)
QtCore.QMetaObject.connectSlotsByName(MainWindowWithDock)
def retranslateUi(self, MainWindowWithDock):
MainWindowWithDock.setWindowTitle(_translate("MainWindowWithDock", "MainWindow", None))
class MainWindowWithButtonDlg(QMainWindow):
pass
class MainWindowWithDockDlg(QMainWindow):
pass
def main():
app = QApplication(sys.argv)
windowWithDockUi = Ui_MainWindowWithDock()
windowWithDock = MainWindowWithDockDlg()
windowWithDockUi.setupUi(windowWithDock)
windowWithDock.show()
app.exec()
# the dock widget should be closed by now
ui = Ui_MainWindowWithButton()
window = MainWindowWithButtonDlg()
ui.setupUi(window)
window.show()
app.exec()
if __name__ == '__main__':
main()
拒绝原始Source的方法.这里我们有一个带有 QMainwindow 的 QDialog 作为中央 Widget - 因此它在某种意义上变成了 QMainWindow(来自 Anki addCards.py(滚动到底部):
reject method of the original Source. Here we have a QDialog with a QMainwindow as it's central Widget - therefore it becomes a QMainWindow in some sense(from Anki addCards.py (scroll to bottom):
def reject(self):
if not self.canClose(): # this way of calling is basically the problem: we might leave this method without doing anything
return
remHook('reset', self.onReset)
remHook('currentModelChanged', self.onModelChange)
clearAudioQueue()
self.removeTempNote(self.editor.note)
self.editor.setNote(None)
self.modelChooser.cleanup()
self.deckChooser.cleanup()
self.mw.maybeReset()
saveGeom(self, "add")
aqt.dialogs.close("AddCards")
QDialog.reject(self)
推荐答案
您可以使用 event-filter 监视给定窗口的所有事件.就在窗口关闭之前,它会总是发布一个关闭事件.窗口是否修改了正常关闭过程并不重要.如果最终关闭,则相应关闭的 accept 属性-event 保证为True
.所以,如果你关注这个事件,你可以简单地检查它是否被接受,然后采取相应的行动.
You can use an event-filter to monitor all the events of a given window. Just before a window closes, it will always post a close-event. It doesn't matter whether the window has modified the normal closing process or not. If and when it eventually closes, the accept property of the corresponding close-event is guaranteed to be True
. So, if you watch for this event, you can simply check to see if it was accepted, and then act accordingly.
要解决的主要问题是如何找到合适的窗口观看.在创建停靠小部件时,这可能无法访问.因此,一种方法是等到首次显示停靠小部件的父级,然后查找顶级窗口并在其上安装事件过滤器.以这种方式做事意味着停靠小部件永远不需要知道它最终依赖的窗口的任何信息.
The main problem to solve is how to find the right window to watch. At the time the dock-widget is created, this may not be accessible. So one approach is to wait until the parent of the dock-widget is first shown, then look for the top-level window and install an event-filter on that. Doing things this way means the dock-widget never needs to know anything about the window it is ultimately dependant upon.
以下是基于您的示例的此方法的工作演示(删除了大部分不相关的内容):
Below is a working demo of this approach based on your example (with most of the irrelevant stuff removed):
import sys
from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import QApplication, QDialog, QMainWindow
class EventWatcher(QtCore.QObject):
def __init__(self, parent):
QtCore.QObject.__init__(self, parent)
parent.installEventFilter(self)
def eventFilter(self, source, event):
if source is self.parent():
if event.type() == QtCore.QEvent.Show:
target = source.parent()
while target.parent() is not None:
target = target.parent()
print('found target window: %r' % target)
source.removeEventFilter(self)
target.installEventFilter(self)
elif event.type() == QtCore.QEvent.Close:
source.closeEvent(event)
print('test filter accepted: %s' % event.isAccepted())
if event.isAccepted():
self.parent().close()
return True
return QtCore.QObject.eventFilter(self, source, event)
class Ui_MainWindowWithDock(object):
def setupUi(self, MainWindowWithDock):
self.theDock = QtGui.QDockWidget(MainWindowWithDock)
MainWindowWithDock.addDockWidget(QtCore.Qt.DockWidgetArea(2), self.theDock)
# add the event watcher
EventWatcher(self.theDock)
class MainWindowWithDockDlg(QMainWindow):
pass
# mock-up class for testing
class MockDialog(QDialog):
def __init__(self):
QDialog.__init__(self)
windowWithDock = MainWindowWithDockDlg()
windowWithDockUi = Ui_MainWindowWithDock()
windowWithDockUi.setupUi(windowWithDock)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(windowWithDock)
self.canClose = False
def reject(self):
if not self.canClose:
self.canClose = True
return
QDialog.reject(self)
def closeEvent(self, event):
QDialog.closeEvent(self, event)
print('test close accepted: %s' % event.isAccepted())
def main():
app = QApplication(sys.argv)
dialog = MockDialog()
dialog.show()
app.exec_()
# the dock widget should be closed by now
window = QMainWindow()
window.show()
app.exec_()
if __name__ == '__main__':
main()
这篇关于parentWidget 关闭时浮动 QDockWidget 不会关闭的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!