PyQt6中代替"QMessageBox.Yes"的选择

6
我正在将我的一个脚本从PyQt5迁移到PyQt6。感谢这个答案,我已经找出了如何移植大部分的代码,但是我遇到了一个问题。
我已经发现,PyQt6使用QtWidgets.QMessageBox.StandardButtons.Yes代替PyQt5的QtWidgets.QMessageBox.Yes
但是,在打开QMessageBox后检查用户是否按下"Yes"时,将QtWidgets.QMessageBox.Yes替换为QtWidgets.QMessageBox.StandardButtons.Yes是不起作用的(请查看下面的示例)。

示例:

PyQt5:

reply = QtWidgets.QMessageBox()
reply.setText("Some random text.")
reply.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)

x = reply.exec_()

if x == QtWidgets.QMessageBox.Yes:
    print("Hello!")

这里正常打印“Hello!”。(16384 == 16384)

PyQt6:

reply = QtWidgets.QMessageBox()
reply.setText("Some random text.")
reply.setStandardButtons(QtWidgets.QMessageBox.StandardButtons.Yes | 
                         QtWidgets.QMessageBox.StandardButtons.No)

x = reply.exec()

if x == QtWidgets.QMessageBox.StandardButtons.Yes:
    print("Hello!")

这里的 "Hello!" 根本没有被打印出来。(16384 != StandardButtons.yes)


我知道我可以这样做:

x = reply.exec()

if x == 16384:
    print("Hello!")

因为在按下"Yes"之后,QMessageBox等于16384 (请参阅此处),但我不想使用这种方法,而是想使用类似PyQt5示例的方式。


1
欢迎来到StackOverflow!你的问题表述清晰,文字流畅,无需道歉! - xjcl
6个回答

6

StandardButtons 不是我可以选择的 QMessageBox 属性/方法。不确定在过去的 4 个月中是否进行了更新,但对我而言,代码应该使用 StandardButton 而不是 StandardButtons

from PyQt6.QtWidgets import QMessageBox

reply = QMessageBox()
reply.setText("Some random text.")
reply.setStandardButtons(QMessageBox.StandardButton.Yes | 
                     QMessageBox.StandardButton.No)

x = reply.exec()

if x == QMessageBox.StandardButton.Yes:
    print("Hello!")

2
新的Enum实现在某些地方创建了一些问题和不一致性,特别是考虑到开发状态。我认为正确的使用需要检查你是否正在使用flagenum。一个Enum(通常有一个单数命名空间)用于表示单个值,而一个Flag(通常是复数)表示可能是按位的结果:QMessageBox.StandardButton.Yes是一个EnumQMessageBox.StandardButtons(QMessageBox.StandardButton.Yes)就像QMessageBox.StandardButton.Yes|QMessageBox.StandardButton.No一样是一个Flag - musicamante

4

QtWidgets.QMessageBox.StandardButtons在PyQt6中使用enum.Flag实现,而QDialog.exec()返回一个int。不幸的是,它们不能直接比较,但您仍然可以使用:

if x == QtWidgets.QMessageBox.StandardButtons.Yes.value:
    print("Hello!")

请注意,惯用的x == int(Yes)也不起作用。
PyQt5使用了一个包装的自定义StandardButtons类(输入Yes | No即可查看此类),而不是enum.IntEnum,另一个答案声称使用了这种方法。然而,IntEnum本来应该是一个合理的选择,因为它特别允许int比较。

3
这有点奇怪。根据QMessageBox.exec的文档:

当使用带有标准按钮的 QMessageBox 时,此函数返回一个 StandardButton 值,表示单击的标准按钮。

你正在使用标准按钮,因此这应该返回一个 QMessageBox.StandardButtons 枚举。
值得一提的是,在 PyQt5 中将整数与枚举进行比较不是问题,因为枚举是使用 enum.IntEnum 实现的。现在,它们使用 enum.Enum 实现。从Riverbank Computing 网站上可以看到:

所有枚举现在都实现为 enum.Enum(PyQt5 对于范围枚举使用 enum.IntEnum 并对于传统命名枚举使用自定义类型)。PyQt5 在期望枚举时允许 int ,但 PyQt6 要求正确的类型。

但是,由于某些原因,QMessageBox.exec 返回一个整数(我刚刚尝试了 PyQt6==6.0.0 版本)!
目前,您可以通过故意从返回的整数构造枚举对象来解决这个问题:
if QtWidgets.QMessageBox.StandardButtons(x) == QtWidgets.QMessageBox.StandardButtons.Yes:
            print("Hello!")

另外,由于你正在比较枚举类型,我建议使用is而不是==


甚至可以将结果与int(QStandardButtons.Yes)进行比较吗? - musicamante
@musicamante 不,int 构造函数会抛出异常。 - xjcl
关于 PyQt5:Python 的原生 IntEnum 没有被使用,而是使用了自定义类型。 - xjcl
那似乎不是很有效或可用。我想我会在PyQt ml上询问更多信息,Phil通常很愿意合作。 - musicamante

1
我知道答案可能有点晚,但这是在pyqt6中适用于我的方法。
msg = QMessageBox()
msg.setIcon(QMessageBox.Icon.Information)

msg.setText('Teste')
msg.setInformativeText("This is additional information")
msg.setWindowTitle("MessageBox demo")
#msg.setDetailedText("The details are as follows:")
msg.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel)
msg.buttonClicked.connect(self.msgbtn)
msg.exec()

def msgbtn(self, i):
    print( "Button pressed is:",i.text() )

这并没有真正帮助,原帖明显需要对按钮角色进行比较,而使用按钮文本是一个糟糕的选择,特别是对于由Qt自动创建的按钮:如果程序在不同的本地化环境下使用,“Yes”按钮返回“Oui”,“Ja”,“Sì”或“да”怎么办? - musicamante

0

这种方式对我也非常有帮助,主要是因为它已经翻译了按钮的文本。

    msgBox = QMessageBox()
    msgBox.setIcon(QMessageBox.Icon.Information)
    msgBox.setWindowTitle('Excluir Ponto')
    msgBox.setText('Tem certeza que deseja excluir o ponto {point}?'.format(point = self.dialogCUDP.UI.lineEditPoint.text()))
    btn_delete = msgBox.addButton('Excluir', QMessageBox.ButtonRole.YesRole)
    btn_cancel = msgBox.addButton('Cancelar', QMessageBox.ButtonRole.NoRole)
    msgBox.exec()

    if msgBox.clickedButton() == btn_delete:
        if self._serverOn:
            if self.GM.dfPointsManager.deletePointPMCSV(Priceformat.setFormatStrFloat(self.dialogCUDP.UI.lineEditPoint.text())):
                QMessageBox.information(self, 'Excluir Ponto', 'Ponto {point}, excluído com sucesso!'.format(point = self.dialogCUDP.UI.lineEditPoint.text()))
                self.disableFieldCUDPoint()
                self.clearFieldCUDPoint()
            else:
                QMessageBox.warning(self, 'Excluir Ponto', 'Não foi possível excluir o ponto {point}!\nPor favor, tente novamente'.format(point = self.dialogCUDP.UI.lineEditPoint.text()))
                msgBox.close()
        else:
            if self._createnewpointPM.deletePointPMCSV(Priceformat.setFormatStrFloat(self.dialogCUDP.UI.lineEditPoint.text())):
                QMessageBox.information(self, 'Excluir Ponto', 'Ponto {point}, excluído com sucesso!'.format(point = self.dialogCUDP.UI.lineEditPoint.text()))
                self.disableFieldCUDPoint()
                self.clearFieldCUDPoint()
            else:
                QMessageBox.warning(self, 'Excluir Ponto', 'Não foi possível excluir o ponto {point}!\nPor favor, tente novamente'.format(point = self.dialogCUDP.UI.lineEditPoint.text()))
                msgBox.close()

不好意思再次打扰你,但是:1. 这并没有“翻译”文本,而是设置了一个硬编码的文本(出于与我在其他评论中解释的相同原因,在概念上是错误的);2. 正如我在对你的其他答案的评论中所说的那样,问题仍然存在:按钮角色的比较有问题;虽然你的解决方案可能有效,但它只是一个变通方法,不能真正解决问题。 - musicamante

0

我遇到了相同的问题(从PyQt5转移到PyQt6),但按照这种方式编码,它运行得很顺畅:

if QtWidgets.QMessageBox.critical(self,"Foo","PROTECTION NOT FOUND - Exit",QtWidgets.QMessageBox.StandardButtons.Yes):
                print("Exit")

我把它用作“关键”、“问题”甚至“信息”,它总是在运行。


这种方法并不是很有效,因为它只适用于静态函数,并且几乎与将exec_()的返回值与基本的if result:进行比较相同,而实际问题是针对动态构建的消息框和与标准按钮集合进行比较,可能有多个并返回不同的值。 - musicamante
你是对的,先前的答案测试值为QtWidgets.QMessageBox.StandardButtons.No或.Abort或.Yes是正确的方式。在我的情况下,我只需要通知,没有选择余地。 - Mauro O

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接