Alternative to "QMessageBox.Yes" for PyQt6
Asked Answered
F

7

7

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.

Fluoridate answered 15/1, 2021 at 11:25 Comment(1)
Welcome to StackOverflow! Your question is clear and well-written, no need to apologize!Starofbethlehem
F
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 ==.

Frugivorous answered 15/1, 2021 at 12:46 Comment(4)
Or even comparing the result with int(QStandardButtons.Yes)?Sorensen
@Sorensen Nope, the int constructor throws an Exception.Starofbethlehem
Re PyQt5: Python's native IntEnums weren't used, but a custom typeStarofbethlehem
That doesn't seem very effective or usable. I'll think I'll ask more information on the PyQt ml, Phil is usually very forthcoming as collaborative.Sorensen
S
7

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!")
Stella answered 29/5, 2021 at 17:36 Comment(1)
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.Sorensen
S
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.

Starofbethlehem answered 17/1, 2021 at 8:22 Comment(0)
F
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 ==.

Frugivorous answered 15/1, 2021 at 12:46 Comment(4)
Or even comparing the result with int(QStandardButtons.Yes)?Sorensen
@Sorensen Nope, the int constructor throws an Exception.Starofbethlehem
Re PyQt5: Python's native IntEnums weren't used, but a custom typeStarofbethlehem
That doesn't seem very effective or usable. I'll think I'll ask more information on the PyQt ml, Phil is usually very forthcoming as collaborative.Sorensen
S
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() )
Supramolecular answered 3/3, 2022 at 9:0 Comment(1)
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 "да"?Sorensen
P
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

Preschool answered 20/1, 2021 at 17:8 Comment(2)
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.Sorensen
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.Preschool
S
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()
Supramolecular answered 6/3, 2022 at 15:31 Comment(1)
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.Sorensen
T
0
pop_up = QMessageBox.question(
            self,
            "Exit Application!",
            "Are you sure you want to exit the Application?",   
        )
        if pop_up == QMessageBox.StandardButton.Yes:
Territerrible answered 20/8, 2024 at 21:36 Comment(2)
Although this code might answer the question, I recommend that you also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes.Drubbing
Well Instead od declaring buttons I have used the simple inbuilt approach wish comes with Yes or No button in PyQt6.Territerrible

© 2022 - 2025 — McMap. All rights reserved.