【问题标题】:floating QDockWidget does not close when parentWidget closes当 parentWidget 关闭时,浮动 QDockWidget 不会关闭
【发布时间】:2018-01-28 23:10:40
【问题描述】:

我有两个打开的 MainWindows:MainWindowWithButton 和 MainWindowWithDock。后者包含一个 QDockWidget。

IS 行为:当用户使 DockWidget 可浮动并关闭 MainWindowWithDock 时,dockWidget 不会关闭。

应该行为:当用户使 DockWidget 可浮动并关闭 MainWindowWithDock 时,dockWidget 也会关闭。

注意事项:

  • “IS 行为”的原因:浮动 DockWidget 似乎独立于其父级
  • 我无法监听 onClose/reject(因为它会在我的特定情况下提供虚假信息。
  • MainWindow 不会发出有关其行为的明确信号
  • 重要的是,DockWidget MainWindow 关闭之前关闭。否则焦点会出乎意料

示例代码:

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的reject方法。这里我们有一个 QDialog 和一个 QMainwindow 作为它的中心 Widget - 因此它在某种意义上变成了一个 QMainWindow(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)

【问题讨论】:

  • 您可以放置​​一个可以执行的示例,我们不想浪费时间猜测您将如何构建您的应用程序,我们需要一个工作示例
  • 我已经尝试过您的代码,在语法中添加了某些更正并且它有效,您可以向我们展示更多代码以更好地理解您。
  • 抱歉浪费您的时间:/我刚刚添加了一个完整的工作示例
  • 创建窗口后为什么在代码中间使用app.exec()
  • 实际上,测试应用程序中的第二个窗口并没有通过单击按钮打开。我想我也可以用这种风格展示行为。如果你喜欢,你可以把它注释掉,这样会同时显示两个窗口。行为保持不变

标签: python python-2.7 pyqt pyqt4


【解决方案1】:

您可以使用event-filter 来监视给定窗口的所有事件。就在窗口关闭之前,它会总是发布一个关闭事件。窗口是否修改了正常的关闭过程并不重要。如果它最终关闭,则相应关闭事件的accept property保证True。因此,如果您关注此事件,您可以简单地检查它是否被接受,然后采取相应的行动。

要解决的主要问题是如何找到合适的窗口来观看。在创建停靠小部件时,这可能无法访问。所以一种方法是等到第一次显示dock-widget的父级,然后寻找顶级窗口并在其上安装一个事件过滤器。以这种方式做事意味着dock-widget永远不需要知道它最终依赖的窗口的任何信息。

以下是基于您的示例的这种方法的工作演示(删除了大部分不相关的内容):

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()

【讨论】:

    猜你喜欢
    • 2015-11-15
    • 2013-02-12
    • 2018-11-26
    • 1970-01-01
    • 1970-01-01
    • 2012-05-13
    • 1970-01-01
    • 1970-01-01
    • 2016-12-13
    相关资源
    最近更新 更多