如何拦截QProgressDialog取消点击

How to intercept QProgressDialog cancel click

我有一个带有取消按钮的标准 QProgressDialog。 If/When 用户点击取消按钮,我不希望对话框立即隐藏,相反我宁愿禁用取消按钮并执行一些清理工作,然后在确定后关闭 QProgressDialog这项工作已经完成。 如何拦截当前函数?

从文档看来我应该覆盖:

PySide.QtGui.QProgressDialog.cancel()

Resets the progress dialog. PySide.QtGui.QProgressDialog.wasCanceled() becomes true until the progress dialog is reset. The progress dialog becomes hidden.

我试过将此方法子类化,但当我单击取消按钮时它似乎甚至没有被调用。

要禁用对话框的按钮,您必须获得对它的引用。由于它是一个基本的QPushButton,你可以使用findChild():

dialog = QProgressDialog(self)
cancelButton = dialog.findChild(QPushButton)
cancelButton.setEnabled(False)

考虑到禁用一个 永远不会 启用的按钮从用户体验的角度来看很烦人,因此更好的选择是 not 展示它,setCancelButton() 解释如何做:

If nullptr is passed, no cancel button will be shown.

在python术语中,nullptr表示None:

dialog = QProgressDialog(self)
dialog.setCancelButton(None)

遗憾的是,这不会阻止用户通过关闭对话框或按 Esc.

来取消对话框

这对 any QDialog 有效,并且,为了正确避免这种情况,subclassing 是更好的选择:你需要防止 拒绝对话框(Esc键)关闭事件。虽然它们的结果相似,但处理方式不同。

覆盖 reject()(什么都不做)会阻止任何会触发拒绝(取消)的操作,包括按 Esc.

覆盖 closeEvent() 需要一个额外的步骤:您必须确保事件是 spontaneous()(由系统触发 - 通常,用户按下 [=95= 的关闭按钮]),并最终忽略它。这是必要的,因为您可能需要调用 close()accept() 实际上 在完成该过程后关闭对话框。

class NonStopProgressDialog(QtWidgets.QProgressDialog):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setCancelButton(None)

    def reject(self):
        pass

    def closeEvent(self, event):
        if event.spontaneous():
            event.ignore()

请注意,没有直接的方法可以知道 自发的 关闭事件是由用户直接触发的(试图关闭 window),还是系统(关机时)。

另请注意,如果您 需要以编程方式关闭对话框,您要么调用 accept(),要么调用基础实现,这样您就可以获取来自对话框 reject():

的正确 return 值
    def rejectNoMatterWhat(self):
        super().reject()

最后,如果出于任何原因您仍然需要取消按钮,则必须断开其信号。

一般来说,这可能会起作用:

dialog = QProgressDialog(self)
cancelButton = dialog.findChild(QPushButton)
cancelButton.disconnect()

但是上面会断开 any 信号到 any 插槽,在某些情况下应该避免这种情况。
我们从 the sources 得知 clicked 信号实际上连接到 canceled() 插槽,因此更好的解决方案是改为执行以下操作:

dialog = QProgressDialog(self)
cancelButton = dialog.findChild(QPushButton)
cancelButton.clicked.disconnect(self.canceled)

由于您可能需要在 parent/main class 中收到通知,更合适的解决方案是在上面使用的子 class 中创建自定义信号:

class NonStopProgressDialog(QtWidgets.QProgressDialog):
    userCancel = QtCore.pyqtSignal()
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        cancelButton = self.findChild(QPushButton)
        cancelButton.clicked.disconnect(self.canceled)
        cancelButton.clicked.connect(
            lambda: cancelButton.setEnabled(False))
        cancelButton.clicked.connect(self.userCancel)

    def reject(self):
        pass

    def closeEvent(self, event):
        if event.spontaneous():
            event.ignore()

class SomeWindow(QtWidgets.QWidget):
    def showProgress(self):
        self.progressDialog = NonStopProgressDialog(self)
        self.progressDialog.userCancel.connect(self.stopSomething)
        # ...

    def stopSomething(self):
        self.progressDialog.setCancelButtonText('Please wait')
        # do something...