I have two open MainWindows: MainWindowWithButton and MainWindowWithDock. The later contains a QDockWidget.
IS behaviour: When the users makes the DockWidget floatable and closes MainWindowWithDock, the dockWidget doesn't close.
SHOULD behaviour: When the users makes the DockWidget floatable and closes MainWindowWithDock, the dockWidget closes as well.
Notes:
Example code :
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()
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)
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()