6

I am trying to port over a script of mine from PyQt5 to PyQt6. I have figured out how to port most of the things thanks to this answer, however, I have run into an issue.

I have figured out that PyQt6 uses QtWidgets.QMessageBox.StandardButtons.Yes instead of PyQt5's QtWidgets.QMessageBox.Yes.

However, when checking if the user pressed "Yes" after a QMessageBox opens, replacing QtWidgets.QMessageBox.Yes with QtWidgets.QMessageBox.StandardButtons.Yes doesn't work (check the examples below).


Examples:

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!")

Printing "Hello!" here works normally. (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!" here doesn't print at all. (16384 != StandardButtons.yes)


I know I could just do:

x = reply.exec()

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

because, after pressing "Yes", the QMessageBox equals to 16384 (see this), but I'd like to not use that approach, and rather use something like the PyQt5 example.

xjcl
  • 12,848
  • 6
  • 67
  • 89
  • 1
    Welcome to StackOverflow! Your question is clear and well-written, no need to apologize! – xjcl Jan 17 '21 at 08:30

6 Answers6

6

StandardButtons is not an Attribute/Method I can choose for QMessageBox. Not sure if this was maybe updated in the last 4 months, but for me the code works with StandardButton instead of 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!")
Sebastian
  • 61
  • 1
  • 1
  • 2
    The new Enum implementation has created some issues and inconsistencies here and there, especially considering the development status. I believe that correct usage requires checking whenever you're using a *flag* or a *enum*. A Enum (which usually has a *singular* namespace) is for the individual values, while a Flag (normally a *plural*) indicates the - possibly bitwise - result: `QMessageBox.StandardButton.Yes` is an *Enum*, `QMessageBox.StandardButtons(QMessageBox.StandardButton.Yes)` is a *Flag* just like `QMessageBox.StandardButton.Yes|QMessageBox.StandardButton.No` also is. – musicamante May 30 '21 at 00:25
4

QtWidgets.QMessageBox.StandardButtons are implemented using enum.Flag in PyQt6, while QDialog.exec() returns an int. Sadly these cannot be directly compared, but you can still use:

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

Note that the idiomatic x == int(Yes) does not work either.

PyQt5 used a wrapped custom StandardButtons class (type in Yes | No to see this), NOT an enum.IntEnum as the other answer is claiming. An IntEnum would have been a logical choice however since it specifically allows int comparisons.

xjcl
  • 12,848
  • 6
  • 67
  • 89
3

This is kind of strange. According to the documentation for QMessageBox.exec:

When using a QMessageBox with standard buttons, this function returns a StandardButton value indicating the standard button that was clicked.

You are using standard buttons, so this should return a QMessageBox.StandardButtons enum.

It's also worth mentioning that comparing integers with enums was not a problem in PyQt5, because enums were implemented with enum.IntEnum. Now, they're implemented with enum.Enum. From the Riverbank Computing website:

All enums are now implemented as enum.Enum (PyQt5 used enum.IntEnum for scoped enums and a custom type for traditional named enums). PyQt5 allowed an int whenever an enum was expected but PyQt6 requires the correct type.

However, for some reason, QMessageBox.exec returns an integer (I just tried it with PyQt6==6.0.0)!

For now, you can get around this by deliberately constructing an enum object from the returned integer:

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

And, since you're comparing enums, I would suggest using is rather than ==.

Paul M.
  • 10,481
  • 2
  • 9
  • 15
1

I know the answer might be a little late, but here's what works for me in 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() )
  • This doesn't really help, the OP clearly needs to do a comparison with button roles, and using the button text is a terrible choice especially for buttons that are automatically created by Qt: what if the program is used with a different localization, and the "Yes" button returns "Oui", "Ja", "Sì" or "да"? – musicamante Mar 03 '22 at 13:30
0

I had the same problem (from PyQt5 to PyQt6) but coding in this way it's running smooth:

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

I used it as 'critical' or 'question' and even 'information' and it's always running

Roshin Raphel
  • 2,612
  • 4
  • 22
  • 40
  • This is not very effective, as it only works for the static functions, and behaves almost the same as comparing the return of `exec_()` with a basic `if result:`, while the problem at hand is for message boxes constructed dynamically and the comparison with the standard buttons set, which might be more than one and return different values. – musicamante Jan 20 '21 at 18:01
  • You are right, the previous answer testing value as QtWidgets.QMessageBox.StandardButtons.No or .Abort or .Yes is the correct way.In my case I have only to inform and no choice left. – Mauro O Jan 22 '21 at 10:45
0

This way it also served me very well, mainly because it already translates the text of the button.

    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()
  • Sorry to bother you again, but: 1. this doesn't "translate" the text, it sets a *hardcoded* one (which is conceptually wrong for the same reason explained in my other comment); 2. as already said in my comment to your other answer, the point remains: the issue is with the comparison of button roles; while your solution *may* work, it is just a work around that doesn't really address the problem. – musicamante Mar 08 '22 at 03:30