1

I want to add a custom button to QMessagebox that opens up a matplotlib window, along with an Ok button for user to click when they want to close it

I currently have it somewhat working, but I want the two buttons to do separate things and not open the window.

I know I can just create a dialog window with the desired results, but I wanted to know how to with a QMessageBox.

import sys
from PyQt5 import QtCore, QtWidgets

def main():
    app = QtWidgets.QApplication(sys.argv)
    msgbox = QtWidgets.QMessageBox()
    msgbox.setWindowTitle("Information")
    msgbox.setText('Test')
    msgbox.addButton(QtWidgets.QMessageBox.Ok)
    msgbox.addButton('View Graphs', QtWidgets.QMessageBox.YesRole)

    bttn = msgbox.exec_()

    if bttn:
        print("Ok")
    else:
        print("View Graphs")
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

Desired result:

Ok button - closes QMessageBox

View Graph button - opens matplotlib window and keeps QMessageBox open until user clicks Ok

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Drees
  • 688
  • 1
  • 6
  • 21

3 Answers3

5

A bit hacky IMO, but after you add the View Graphs button you could disconnect its clicked signal and reconnect it to your slot of choice, e.g.

import sys
from PyQt5 import QtCore, QtWidgets

def show_graph():
    print('Show Graph')

def main():
    app = QtWidgets.QApplication(sys.argv)
    msgbox = QtWidgets.QMessageBox()
    msgbox.setWindowTitle("Information")
    msgbox.setText('Test')
    msgbox.addButton(QtWidgets.QMessageBox.Ok)

    yes_button = msgbox.addButton('View Graphs', QtWidgets.QMessageBox.YesRole)
    yes_button.clicked.disconnect()
    yes_button.clicked.connect(show_graph)

    bttn = msgbox.exec_()

    if bttn:
        print("Ok")
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()
Heike
  • 24,102
  • 2
  • 31
  • 45
  • In case you are using PyQt6, is `QMessageBox.ButtonRole.YesRole` instead of `QMessageBox.YesRole`. If you do not want the message box to keep showing, just avoid the `button.clicked.disconnect()` statement – Esteban Jan 10 '23 at 20:28
4

A QMessageBox, as all QDialogs, blocks everything until exec_() is returned, but it also automatically connects all buttons to either accepted/rejected signals, returning the exec_() in any case.

A possible solution for your code is:

app = QtWidgets.QApplication(sys.argv)
msgbox = QtWidgets.QMessageBox()
# the following is if you need to interact with the other window
msgbox.setWindowModality(QtCore.Qt.NonModal)
msgbox.addButton(msgbox.Ok)
viewGraphButton = msgbox.addButton('View Graphs', msgbox.ActionRole)
# disconnect the clicked signal from the slots QMessageBox automatically sets
viewGraphButton.clicked.disconnect()

# this is just an example, replace with your matplotlib widget/window
graphWidget = QtWidgets.QWidget()

viewGraphButton.clicked.connect(graphWidget.show)
msgbox.button(msgbox.Ok).clicked.connect(graphWidget.close)
# do not use msgbox.exec_() or it will reset the window modality
msgbox.show()
sys.exit(app.exec_())

That said, be careful in using QDialog.exec_() outside (as in "before") the sys.exit(app.exec_()) call, as it might result in unexpected behavior if you don't know what you are doing.

musicamante
  • 41,230
  • 6
  • 33
  • 58
  • Is there a way to push the QMessageBox back? When I open the graph window, the QMessageBox is always on top and wont let me click on graph window until I close it – Drees Jul 03 '19 at 14:58
  • 1
    That's strange, in my tests (Linux and Wine), using a simple QWidget, it was possible to interact with the other widget too. It's possible that on Windows or MacOS the window stays modal, but setting the modality as shown in the code should prevent that. Did you leave the messagebox without any parent, and are you sure you are using `show()` instead of `exec_()`? – musicamante Jul 03 '19 at 15:23
0

Okay well first you do not use anything in QtCore so no need to import that. This should help you understand what you need to do. I tweaked it a smidge and I had to add the 2 sys.exits or when you pressed View Graphs the program just hung due to how you have this currently set up. If you do not choose to adjust this code flow then pull the sys.exit out of the if/else and put it right after the if/else -- no sense having unnecessary redundant code

from sys import exit as sysExit

from PyQt5.QtWidgets import QApplication, QMessageBox

def Main():
    msgbox = QMessageBox()
    msgbox.setWindowTitle("Information")
    msgbox.setText('Test')
    msgbox.addButton(QMessageBox.Ok)
    msgbox.addButton('View Graphs', QMessageBox.YesRole)

    bttn = msgbox.exec_()

    if bttn == QMessageBox.Ok:
        print("Ok")
        sysExit()
    else:
        print("View Graphs")
        sysExit()

if __name__ == "__main__":
    MainThred = QApplication([])

    MainApp = Main()

    sysExit(MainThred.exec_())

Aka your non-redundant if/else would look as follows

    if bttn == QMessageBox.Ok:
        print("Ok")
    else:
        print("View Graphs")

    sysExit()
Dennis Jensen
  • 214
  • 1
  • 14