4

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:

  • 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

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)
TheTrowser
  • 363
  • 4
  • 14
  • You can place an example that can be executed, we do not want to waste time guessing how you will have built your application, we need a working example – eyllanesc Aug 20 '17 at 23:18
  • I have tried your code adding certain corrections in the syntax and it works, you could show us more code to understand you better. – eyllanesc Aug 20 '17 at 23:29
  • Sorry for wasting your time :/ I just added a full working example – TheTrowser Aug 20 '17 at 23:34
  • Why do you use `app.exec()` in the middle of the code after creating a window? – eyllanesc Aug 20 '17 at 23:58
  • Actually the second window in the test app didn't open with a buttonclick. And I figured I can show the behaviour with this style as well. If you like, you can comment it out, so two windows will be shown at the same time. The behaviour stays the same though – TheTrowser Aug 21 '17 at 00:01
  • `exec()` what you do is block the execution and only unlock it when you close the first window, so you notice that the second window opens. What do you want to do? – eyllanesc Aug 21 '17 at 00:02
  • Do you want the QDockWidget to close when the QMainWindow containing it closes? – eyllanesc Aug 21 '17 at 00:04
  • yes! this is what I want. – TheTrowser Aug 21 '17 at 00:09
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/152406/discussion-between-thetrowser-and-eyllanesc). – TheTrowser Aug 21 '17 at 00:11
  • If you show me how you have implemented the method or share the modified class I could give you a solution but your question falls on the off topic: **"why isn't this code working?" must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers** more information in https://stackoverflow.com/help/on-topic – eyllanesc Aug 21 '17 at 00:39
  • Does the class that you comment emit any signal when it closes or calls some function? – eyllanesc Aug 21 '17 at 00:39
  • You could explain me better, in which cases the window is not closed since with the little information it provides it is impossible to solve it, since that is the method that is called ultimately to close the window, although catch the event maybe close its window Close for what you comment. – – eyllanesc Aug 21 '17 at 00:39
  • Is it a QDialog or QMainWindow? – eyllanesc Aug 21 '17 at 00:55
  • My solution still works, you just have to create the signal in the class you are talking about, and issue it after the return, since at that moment we know that it will close. – eyllanesc Aug 21 '17 at 00:56
  • You confuse me, first you talk about QMainWindow and then you show a link with the QDialog class, you could show a correct code or share your complete project to understand you better, without this I will not be able to help you and I do not think anyone will help you. – eyllanesc Aug 21 '17 at 01:07

1 Answers1

1

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()
ekhumoro
  • 115,249
  • 20
  • 229
  • 336