QMessageBox add custom button and keep open - python

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

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()

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.

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()

Related

Printing some text in a Label, depending on textbox input - PyQT5

I need help figuring out how to use the value written in a textbox in PyQT5, and use that value to build an IF statement. Any suggestions on how to do it? I have tried to declare the text in the textbox as a variable and use it in the IF statement but I can't seem to figure it out how to do it properly, and every time i run the code, some exit code shows (-1073741819 (0xC0000005) ).
Summing up, can't use pass the value of the textbox to the variable in order to do an IF statement.
I had this code down below:
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QTextEdit
def window():
app = QApplication(sys.argv)
win = QMainWindow()
win.setGeometry(200, 200, 400, 400)
win.setWindowTitle("Register Program")
label = QtWidgets.QLabel(win)
label.setText("Random Text")
label.move(169, 15)
label2 = QtWidgets.QLabel(win)
label2.resize(300, 100)
label2.setText("1- Register new person\n2- See all regestries\n3- See last regestry\n\nPress ESC to exit\n")
label2.move(70, 50)
textbox = QtWidgets.QLineEdit(win)
textbox.setText("")
textbox.resize(250, 25)
textbox.move(70, 250)
button1 = QtWidgets.QPushButton(win)
button1.move(150, 300)
button1.setText("Submit")
button1.clicked.connect(clicked)
button2 = QtWidgets.QPushButton(win)
button2.move(150, 335)
button2.setText("Close")
button2.clicked.connect(close)
win.show()
sys.exit(app.exec_())
def clicked():
inpt = int(window().textbox.text)
if inpt == 1:
print("Hello")
def close():
sys.exit()
window()```
If you're just looking to get user input, there's a builtin static method you can call for requesting input of a particular type: https://doc.qt.io/qt-5/qinputdialog.html#getText
If you want to make your own widget however, you need to use the signals and slots to trigger a python method to store the value. This is easiest to do in a class. You can trigger the method whenever the text changes with the textChanged signal and do whatever you need to do with it.
(Note, I haven't run this as I don't have PyQt5 currently installed, but it should work)
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
# type: (QtWidgets.QWidget) -> None
super(Widget, self).__init__(parent)
self.line_edit = QtWidgets.QLineEdit()
main_layout = QtWidgets.QVBoxLayout()
main_layout.addWidget(self.line_edit)
self.setLayout(main_layout)
self.line_edit.textChanged.connect(self.on_text_changed)
def get_text(self):
return self.line_edit.text()
def on_text_changed(self, text):
print("The text was changed to:", text)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
widget = Widget()
widget.show()
app.exec_()
Edit: Also, to clarify why you're getting an error, QApplication is a singleton. This means there can only ever be one created. If you try to create a second, you'll get an error. The best way to access the current QApplication is to call QApplication.instance(). You also only call app.exec_() once, as once the application is running it will continue to run in the background. You should use signal/slots to interact with the UI and trigger the code you want to run.

Connecting in PyQt4 QMessageBox fails to call the slot method

I was trying to analyse the sample code cited here: PyQt - QMessageBox
Here's the snippet:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Window(QMainWindow):
def __init__(self):
super().__init__()
w = QWidget()
b = QPushButton(self)
b.setText("Show message!")
b.clicked.connect(self.showdialog)
w.setWindowTitle("PyQt Dialog demo")
def showdialog(self):
msg = QMessageBox()
msg.setIcon(QMessageBox.Question)
# self.connect(msg, SIGNAL('clicked()'), self.msgbtn)
msg.buttonClicked.connect(self.msgbtn)
msg.exec_()
def msgbtn(self, i):
print("Button pressed is:", i.text())
if __name__ == '__main__':
app = QApplication([])
w = Window()
w.show()
app.exec_()
There are two ways of connecting signals to slots in PyQt. For buttons, it's:
QtCore.QObject.connect(button, QtCore.SIGNAL(“clicked()”), slot_function)
or
widget.clicked.connect(slot_function)
Using it the second way works fine: the msgbtn slot method is called as intended. However, if I try to change it to the more usual, 'PyQt-onic' way of connecting (i.e. the first one - I commented it out in the snippet), the slot method is never called. Could anyone please help me out with this?
the signal you pass to SIGNAL is incorrect, the QMessageBox does not have the clicked signal but the signal is buttonClicked (QAbstractButton *) so the correct thing is:
self.connect(msg, SIGNAL("buttonClicked(QAbstractButton *)"), self.msgbtn)
On the other hand that is not the PyQt-onic style, but the old style which is not recommended to use, but we recommend using the new style.
Old style:
self.connect(msg, SIGNAL("buttonClicked(QAbstractButton *)"), self.msgbtn)
New style:
msg.buttonClicked.connect(self.msgbtn)
For more detail read the docs.

PyQt - QPushButton loop

I'm new to Python and PyQt so sorry if I can't describe my problems clearly. I want to do a cinema seat selector UI and this is the code I have made below:
import sys
from PyQt5 import uic
from PyQt5.QtWidgets import (QWidget, QApplication)
class Ui2(QWidget):
def __init__(self):
super(Ui2, self).__init__()
uic.loadUi('seat.ui', self)
self.A1.setStyleSheet("background-color: red")
self.B1.clicked.connect(self.greenButton)
self.show()
def greenButton(self):
self.B1.setStyleSheet("background-color: green")
self.B1.clicked.connect(self.whiteButton)
def whiteButton(self):
self.B1.setStyleSheet("background-color: white")
self.B1.clicked.connect(self.greenButton)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Ui2()
sys.exit(app.exec_())
The problem is, if I click the button B1 multiple times the program will freeze - I read it somewhere that this is caused by full memory.
Also this is only for the button B1, what should I do if I want to implement the same functions for all buttons?
Thanks a lot!
You shouldn't call self.B1.clicked.connect so many times. Each time you call that function, it registers another event handler. When the button is clicked, all the event handlers that have ever been registered get called. So as you keep clicking, each click causes more and more things to happen. Eventually it fails.
In general you want to try to have one handler for each event. Here is one simple way to do that:
import sys
from PyQt5 import uic
from PyQt5.QtWidgets import (QWidget, QApplication)
class Ui2(QWidget):
def __init__(self):
super(Ui2, self).__init__()
uic.loadUi('seat.ui', self)
self.b1_color = "green"
self.A1.setStyleSheet("background-color: red")
self.B1.clicked.connect(self.onButton)
self.show()
def onButton(self):
if self.b1_color == "green":
self.b1_color = "white"
else:
self.b1_color = "green"
self.B1.setStyleSheet("background-color: " + self.b1_color)
Tested with PySide and Qt4.8, but it should still work in your environment (I hope).

Move QtWidgets.QtWidget using mouse

I want to move a QtWidgets.QtWidget using the mouse (not a QPushButton, QLabel etc.). I've searched everywhere on the web, but couldn't find an answer for this. mousePressEvent seemed to be the way, but it doesn't work.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_hGUI(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
def setupUi(self, hGUI):
hGUI.setObjectName("hGUI")
hGUI.resize(161, 172)
hGUI.setMinimumSize(QtCore.QSize(200, 200))
hGUI.setMaximumSize(QtCore.QSize(200, 200))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
hGUI = QtWidgets.QWidget()
ui = Ui_hGUI()
ui.setupUi(hGUI)
hGUI.show()
sys.exit(app.exec_())
I'm using Python 3.5, I'm creating the GUI using Qt Designer, then translate it to python code.
Edit: I'm trying to move a borderless windows by click on it.
That's a really simple question sir,
Let's say you just have to have an variable that holds the position of your widget and interact with it according to your needs.
This position variable let's call it "oldPos".
Now inside your mouse press you update this position.
By the last but not least, you relate your "oldPos" and your mouseMove actual position and move your widget.
Wallahhhh, here we have a beautiful and simple movable widget by mouse events.
Here is the simplest example.
from PyQt5.QtWidgets import QWidget
class MyMovableWidget(QWidget):
"""WToolBar is a personalized toolbar."""
homeAction = None
oldPos = QPoint()
def __init__(self):
super().__init__()
def mousePressEvent(self, evt):
"""Select the toolbar."""
self.oldPos = evt.globalPos()
def mouseMoveEvent(self, evt):
"""Move the toolbar with mouse iteration."""
delta = QPoint(evt.globalPos() - self.oldPos)
self.move(self.x() + delta.x(), self.y() + delta.y())
self.oldPos = evt.globalPos()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
coolWidget = MyMovableWidget()
coolWidget.show()
sys.exit(app.exec_())
So simple isn't it? :D
I managed to make it work, thanks to #bnaecker for telling me that the code actually creates two widgets, I've replaced some stuff in my code. Basically, just edit the code generated when you translate the .ui to .py so it would only create one widget.
The most changes happened here:
if __name__ == "__main__":
import sys
sys.excepthook = excepthook
app = QtWidgets.QApplication(sys.argv)
hGUI = QtWidgets.QWidget(flags=QtCore.Qt.FramelessWindowHint)
ui = Ui_hGUI()
ui.setupUi(hGUI)
hGUI.show()
sys.exit(app.exec_())
Edited to this:
if __name__ == "__main__":
sys.excepthook = excepthook
app = QtWidgets.QApplication(sys.argv)
hGUI = Ui_hGUI()
sys.exit(app.exec_())
Add self.show() at the end of retranslateUi(self), replace every "hGUI" in the code with "self" or delete it if it's an argument (except for controls like buttons and labels).
Here are both codes, non-working one vs. working one: https://gist.github.com/anonymous/0707b4fef11ae4b31cf56dc78dd3af80
Note: In the new code, the app is called "VirtualMemories".

PySide / PyQt detect if user trying to close window

is there a way to detect if user trying to close window?
For example, in Tkinter we can do something like this:
def exit_dialog():
#do stuff
pass
root = Tk()
root.protocol("WM_DELETE_WINDOW", exit_dialog)
root.mainloop()
Thanks.
Override the closeEvent method of QWidget in your main window.
For example:
class MainWindow(QWidget): # or QMainWindow
...
def closeEvent(self, event):
# do stuff
if can_exit:
event.accept() # let the window close
else:
event.ignore()
Another possibility is to use the QApplication's aboutToQuit signal like this:
app = QApplication(sys.argv)
app.aboutToQuit.connect(myExitHandler) # myExitHandler is a callable
If you just have one window (i.e the last window) you want to detect then you can use the setQuitOnLastWindowClosed static function and the lastWindowClosed signal.
from PySide2 import QtGui
import sys
def keep_alive():
print("ah..ah..ah..ah...staying alive...staying alive")
window.setVisibility(QtGui.QWindow.Minimized)
if __name__ == '__main__':
app = QtGui.QGuiApplication()
app.setQuitOnLastWindowClosed(False)
app.lastWindowClosed.connect(keep_alive)
window = QtGui.QWindow()
window.show()
sys.exit(app.exec_())
Hope that helps someone, it worked for me as on my first attempt I couldn't override closeEvent(), lack of experience!

Categories