Have a good day to all,
I'm trying to create a exit button in menuBar(). My point is, when user click the close button, QMessageBox() will be pop up to ask QMessageBox.Yes | QMessageBox.No. According to signal, I want to close the program.
To test the code, I just use print(). However results is &No or &Yes, rather than only No or Yes. What is the reason of that ? I couldn't figure out.
Here is my code,
from PyQt5.QtWidgets import *
import sys
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.ui()
self.menu()
self.show()
def ui(self):
self.setWindowTitle("Basic")
self.setGeometry(100, 50, 1080, 640)
def menu(self):
mainmenu = self.menuBar()
filemenu = mainmenu.addMenu("File")
file_close = QAction("Close", self)
file_close.setShortcut("Ctrl+Q")
file_close.triggered.connect(self.close_func1)
filemenu.addAction(file_close)
def close_func1(self): # Ask Yes | No Question
msg = QMessageBox()
msg.setWindowTitle("Warning!")
msg.setText("Would you like to exit ?")
msg.setIcon(QMessageBox.Question)
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
msg.setDefaultButton(QMessageBox.No)
msg.buttonClicked.connect(self.close_func2)
x = msg.exec()
def close_func2(self, i): # In this section code decide to close it or not with if statement
print(i.text())
app = QApplication(sys.argv)
w = Window()
sys.exit(app.exec_())
If you want to decide the outcome of the program based on the result of the button clicked, the simplest solution is to check the result of the exec() method, which returns a StandardButton enumeration (and not a DialogCode as a QDialog normally does).
def close_func1(self): # Ask Yes | No Question
# ...
x = msg.exec()
if x == msg.Yes:
QApplication.quit()
Related
The following running code gives a window with a button on it . The tooltip displays when the mouse enters the button.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtTest import QTest
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.button = QPushButton("MyButton")
global i
i = 0
self.button.setToolTip(str(i) + " seconds has passed since you move your mouse onto MyButton")
self.button.leaveEvent = self.clear()
self.layout.addWidget(self.button)
timer = QTimer(self)
timer.timeout.connect(self.start_counting)
timer.start(1000)
self.widget = QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
def clear(self):
global i
i = 0
def start_counting(self):
if self.button.underMouse() == True:
global i
i = i + 1
self.button.setToolTip(str(i) + " seconds has passed since you move your mouse onto MyButton")
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit( app.exec_() )
My goal is to count the number of seconds the mouse is inside the button and live display it using the tooltip. More precisely, I need to make sure all of the following is happening:
Every time the mouse enters the button, the count starts at 0 seconds and the tooltip shows up.
While the mouse stays inside the button (stationary or moving), the tooltip stays shown with the number of seconds (the text displayed inside the tooltip) updated over time.
When the mouse leaves the button, the count is cleared to zero.
As seen in the code, I have attempted to use underMouse to achieve my goals. My attempt is a partial success as the tooltip does update itself when the mouse moves inside the button. However, the tooltip does not update itself when the mouse stays stationary inside the button. Also, the count does not seem to be cleared when the mouse moves outside of the button .
What am I missing ?
One solution is to use an event-filter to monitor the enter and leave events, and also use an elapsed-timer to get an accurate measure of how long the mouse has been over the target widget. Below is a basic demo based on your example that implements the above. It also tries to match the normal behaviour of tooltips, but if necessary you can easily adjust the code to suit your own needs:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.button = QPushButton("MyButton")
self.layout.addWidget(self.button)
self.widget = QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
self.position = QPoint()
self.counter = QElapsedTimer()
self.timer = QTimer()
self.timer.timeout.connect(self.start_counting)
self.button.installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == QEvent.Enter and source is self.button:
self.counter.start()
self.timer.start(1000)
QTimer.singleShot(500, self.start_counting)
elif event.type() == QEvent.Leave and source is self.button:
self.timer.stop()
QToolTip.hideText()
return super().eventFilter(source, event)
def start_counting(self):
if self.button.underMouse():
if not QToolTip.isVisible():
self.position = QCursor.pos()
count = int(self.counter.elapsed() / 1000)
QToolTip.showText(self.position, (
f'{count} seconds have passed since '
'you moved your mouse onto MyButton'
))
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit( app.exec_() )
So I've been trying my luck with PyQT5 to give a GUI to an app I've been working on.
I've encountered an issue with QMessageBox feature.
I've been trying to create an "Exit" Action on the MenuBar of the app.
And at first I only made it exit when clicked and it worked.
Now I want to make it give a pop up message of "Are you sure?", which is exactly what the QMessageBox does. So this is my code now:
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.ui = uic.loadUi('rent_creation.ui', self)
self.home()
def home(self):
self.ui.actionExit.triggered.connect(self.close_application)
self.show()
def close_application(self):
choice = QMessageBox.question(self, 'Quit?',
"Are you sure you want to quit?",
QMessageBox.Yes | QMessageBox.No)
if choice == QMessageBox.Yes:
sys.exit()
else:
pass
Now every time I click on the Exit button when I run this code, The Python crashes.
I'm not sure what I'm doing wrong... I've been looking around the internet and it all look well.... I've tried all the variation possible of passing QmessageBox (for example I tried adding QWidgets.QMessageBox.Yes/No and it didn't fix this issue).
I've been following a tutorial on the internet where This code is practically the same as his, and it works for him in the tutorial somehow.
caveat: I am on linux, so things are likely a bit different.
However I wouldn't be surprised if the problem is related with the fact that you use sys.exit to quit the GUI. You probably should cleanly close the window, the QApplication and then exit the program.
The following example might solve your issue. Since I don't have you ui file, I just added a menu action to close the the window and connect it with the QMainWindow.close slot and then override the closeEvent method. See the comments in the code:
import sys
from PyQt5 import QtWidgets
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.home()
def home(self):
# add a menu bar with a File menu and a Close action
menu_bar = QtWidgets.QMenuBar(self)
menu = QtWidgets.QMenu('File', menu_bar)
menu_bar.addMenu(menu)
action = menu.addAction('Close')
# connect the Close action with the QMainWindow.close slot
action.triggered.connect(self.close)
self.setMenuBar(menu_bar)
def closeEvent(self, event):
"""override the QMainWindow.closeEvent method to:
* fire up a QMessageBox with a question
* accept the close event if the user click yes
* ignore it otherwise.
Parameters
----------
event : QtCloseEvent
emitted when someone or something asks to close the window
"""
if self.ask_quit():
event.accept()
else:
event.ignore()
def ask_quit(self):
choice = QtWidgets.QMessageBox.question(self, 'Quit?',
"Are you sure you want to quit?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
return choice == QtWidgets.QMessageBox.Yes
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.resize(250, 150)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())
The above way of closing the window, i.e. using the closeEvent and connect the menu action to close, has the advantage that the confirmation box is opened every time someone asks to close the window, independently of the method: you get the message box also clicking on the window X button or with alt+F4
Edit: example of how to cleanly close the QApplication only from the Close menu. This should be more in line with the original behavior of the app in the question (see comment).
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.home()
def home(self):
menu_bar = QtWidgets.QMenuBar(self)
menu = QtWidgets.QMenu('File', menu_bar)
menu_bar.addMenu(menu)
action = menu.addAction('Close')
# connect the Close menu to the ``ask_quit`` slot to ask and exit the
# application on "yes"
action.triggered.connect(self.ask_quit)
self.setMenuBar(menu_bar)
def closeEvent(self, event):
"""Ignore all ways of closing"""
event.ignore()
#QtCore.pyqtSlot()
def ask_quit(self):
choice = QtWidgets.QMessageBox.question(self, 'Quit?',
"Are you sure you want to quit?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
if choice == QtWidgets.QMessageBox.Yes:
QtWidgets.QApplication.quit()
Is there any way to run code after the OK button is pressed but before the dialog is closed in a GTK dialog? I want to be able to syntax check some code entered into the dialog after the OK button is pressed, with the option to keep the dialog open if the code doesn't compile. After a bit of googling I was able to find How to avoid closing of Gtk.Dialog in Python?, but the answer was regrettably short of details, so I couldn't figure out how to implement this. How does one go about doing this?
EDIT: Although the linked question asks about Python specifically, I don't actually care about any particular language. I'm using the Haskell bindings, but I'm fine with answers in any language with GTK+ bindings.
EDIT: If you find this question trying to figure out how to do validation, but don't have the complex requirements I have, I highly recommend looking at #AlexanderDmitriev's answer below.
I'm adding another answer in order to leave previous answer valid.
I see 2 ways to achieve desired behaviour.
Use deprecated gtk_dialog_get_action_area and pack a button there
Stop signal emission to prevent GtkDialog from "seeing" that the response button was pressed.
Both ways can be found in code below. Find deprecated for 1st approach and awesome for 2nd
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class DialogExample(Gtk.Dialog):
button_state = True
def awesome_cb (button, de):
if de.button_state:
print("Awesome ok")
else:
print("Awesome Not allowed")
button.stop_emission_by_name ("clicked")
def deprecated_cb (button, de):
if de.button_state:
print("Deprecated ok")
de.response(11)
else:
print("Deprecated Not allowed");
def switch_state(button, de):
de.button_state = not de.button_state
de.dialog_ok_btn.set_sensitive (de.button_state)
def __init__(self, parent):
Gtk.Dialog.__init__(self, "My Dialog", parent, 0,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OK, Gtk.ResponseType.OK))
self.set_default_size(150, 100)
label = Gtk.Label("This is a dialog to display additional information")
box = self.get_content_area()
state_switcher_btn = Gtk.Button ("Switch")
state_switcher_btn.connect ("clicked", DialogExample.switch_state, self)
box.add(label)
box.add(state_switcher_btn)
hard_work_button = Gtk.Button ("deprec")
hard_work_button.connect ("clicked", DialogExample.deprecated_cb, self)
carea = self.get_action_area()
carea.add (hard_work_button)
tfb = Gtk.Button ("awesome");
tfb.connect("clicked", DialogExample.awesome_cb, self)
self.add_action_widget(tfb, 12)
self.dialog_ok_btn = self.get_widget_for_response (Gtk.ResponseType.OK)
self.show_all()
def do_response (self, response_id):
print ("Response! ID is ", response_id)
class DialogWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Dialog Example")
self.set_border_width(6)
button = Gtk.Button("Open dialog")
button.connect("clicked", self.on_button_clicked)
self.add(button)
def on_button_clicked(self, widget):
dialog = DialogExample(self)
response = dialog.run()
if response == Gtk.ResponseType.OK:
print("The OK button was clicked")
elif response == Gtk.ResponseType.CANCEL:
print("The Cancel button was clicked")
dialog.destroy()
win = DialogWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
Looks like GtkDialog itself doesn't allow to cancel button press (which is OK from user's point of view). However, every time user changes something, you can check it and make buttons sensitive or not. I've extended code from answer to mentioned question
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class DialogExample(Gtk.Dialog):
#this variable controls, whether OK is sensitive
button_state = True
def switch_state(button, de):
print ("switcher")
de.button_state = not de.button_state
de.set_response_sensitive (Gtk.ResponseType.OK, de.button_state)
def __init__(self, parent):
Gtk.Dialog.__init__(self, "My Dialog", parent, 0,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OK, Gtk.ResponseType.OK))
self.set_default_size(150, 100)
label = Gtk.Label("This is a dialog to display additional information")
box = self.get_content_area()
# a button to switch OK's sensitivity
state_switcher_btn = Gtk.Button ("Switch")
state_switcher_btn.connect ("clicked", DialogExample.switch_state, self)
box.add(label)
box.add(state_switcher_btn)
self.show_all()
def do_response (self, response_id):
print ("Override! ID is ", response_id)
class DialogWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Dialog Example")
self.set_border_width(6)
button = Gtk.Button("Open dialog")
button.connect("clicked", self.on_button_clicked)
self.add(button)
def on_button_clicked(self, widget):
dialog = DialogExample(self)
response = dialog.run()
if response == Gtk.ResponseType.OK:
print("The OK button was clicked")
elif response == Gtk.ResponseType.CANCEL:
print("The Cancel button was clicked")
dialog.destroy()
win = DialogWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
Based on the Alexander Dmitriev's hint to use button.stop_emission_by_name, I came up with this solution which is probably what you were asking for:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class MyDialog(Gtk.Dialog):
def __init__(self, *args, **kwargs):
super(MyDialog, self).__init__(*args, **kwargs)
self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OK, Gtk.ResponseType.OK)
self.connect("response", self._cb_response)
def _cb_response(self, widget, response_id):
if response_id == Gtk.ResponseType.OK and self._check_invalid():
msg = Gtk.MessageDialog(
parent=self,
text="There are errors in what you entered.\n\n"
"Are you sure you want to continue?",
message_type=Gtk.MessageType.QUESTION,
buttons=Gtk.ButtonsType.YES_NO,
)
response = msg.run()
msg.destroy()
if response == Gtk.ResponseType.NO:
widget.stop_emission_by_name("response")
return True
return False
def _check_invalid(self):
"""Placeholder for checking for problems"""
return True
dialog = MyDialog()
dialog.run()
I once had this as well. I decided to catch the response signal. I had a function that would handle the validation. However, the function that handles the response signal always returns True to show GTK that the signal has already been handled and the dialog doesn't close. If the dialog needed closing, I did so manually.
myDialogWindow.connect("response", validate_response)
def validate_response(dialog, response_id):
# validate
if correct:
dialog.destroy()
else:
print("Something went wrong")
return True
Though this gets the job done, I'm not certain this the most GTK'ish solution.
Just starting out with Python3 and PyQt5 and I'm kinda stuck here.
My main window takes two ticker codes as input and, after the user presses the Show Me! button, outputs ratio averages for each of them. I created a QMessageBox with an OK button that pops up when the user enters invalid ticker codes.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import good_morning as gm
import MainUI
class MainWindow(QMainWindow, MainUI.Ui_MyStockratios):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.home()
def home(self):
#If Show Me! button is clicked, go grab_user_input()
self.show_me_btn.clicked.connect(self.grab_user_input)
def grab_user_input(self):
#Grab user input for QLineEdits
self.ticker1_value = self.ticker1_label.text()
self.ticker2_value = self.ticker2_label.text()
#Fetch the ratios and place them in a dataframe
self.kr = gm.KeyRatiosDownloader()
try:
self.kr_frame1 = self.kr.download(self.ticker1_value)
self.kr_frame2 = self.kr.download(self.ticker2_value)
#Error handling
except ValueError:
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText("Invalid ticker code")
msg.setInformativeText("Please verify the data you entered and try again.")
msg.setWindowTitle("Error")
msg.setStandardButtons(QMessageBox.Ok)
reply = msg.exec_()
if reply:
self.ticker2_label.clear()
self.ticker1_label.clear()
self.home()
[...]
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
if __name__ == "__main__":
main()
Here's my problem: I want the application to return to its' initial state after the user presses the QMessageBox's OK button, which means the QLineEdits must be cleared and the application must wait for the user to input new data and press the Show Me! button again. I cleared the QLineEdits with the clear() function, but can't seem to make the application wait for new user input.
Thanks in advance !
For future reference you're posted code is a bit incomplete. I took some liberties to get a working example. You can ignore most of the changes except for the button handler part. You only need to connect the button once. Your home() method is not needed.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
# import good_morning as gm
# import MainUI
class MainWindow(QMainWindow):#, MainUI.Ui_MyStockratios):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
# self.setupUi(self)
layout = QVBoxLayout()
widget = QWidget(self)
self.setCentralWidget(widget)
widget.setLayout(layout)
self.show_me_btn = QPushButton('Show Me!', self)
layout.addWidget(self.show_me_btn)
self.ticker1_label = QLineEdit(self)
layout.addWidget(self.ticker1_label)
self.ticker2_label = QLineEdit(self)
layout.addWidget(self.ticker2_label)
# self.home()
self.show_me_btn.clicked.connect(self.grab_user_input)
# def home(self):
# #If Show Me! button is clicked, go grab_user_input()
# self.show_me_btn.clicked.connect(self.grab_user_input)
def grab_user_input(self):
#Grab user input for QLineEdits
self.ticker1_value = self.ticker1_label.text()
self.ticker2_value = self.ticker2_label.text()
# #Fetch the ratios and place them in a dataframe
# self.kr = gm.KeyRatiosDownloader()
#
# try:
# self.kr_frame1 = self.kr.download(self.ticker1_value)
# self.kr_frame2 = self.kr.download(self.ticker2_value)
#
# #Error handling
# except ValueError:
if 1:
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText("Invalid ticker code")
msg.setInformativeText("Please verify the data you entered and try again.")
msg.setWindowTitle("Error")
msg.setStandardButtons(QMessageBox.Ok)
reply = msg.exec_()
if reply:
self.ticker2_label.clear()
self.ticker1_label.clear()
# self.home()
# [...]
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
if __name__ == "__main__":
main()
I've build a QDialog Widget. My problem is, I can't quit the QDialog.
If I press one of the buttons, then the QDialog is only set to "hide".
Here is a little part of the code. It is executable.
I don't know what I'm doing wrong. Maybe one of you can tell me.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class MyClass(QDialog):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
# init
# ------------------------------------------------
self.setMinimumWidth(600)
self.setWindowTitle("Select Dingsda")
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.layoutWidget = QWidget(self)
self.liste = []
# widgets and layouts
# ------------------------------------------------
tempLayout = QHBoxLayout()
self.cancelButton = QPushButton("Cancel")
self.connect(self.cancelButton, SIGNAL('clicked()'), self.cancel)
self.addSelectedButton = QPushButton("Add Selected")
self.connect(self.addSelectedButton, SIGNAL('clicked()'), self.addSelected)
tempLayout.addStretch()
tempLayout.addWidget(self.cancelButton)
tempLayout.addWidget(self.addSelectedButton)
self.layout.addLayout(tempLayout)
# test-data
# ------------------------------------------------
# methods
# ------------------------------------------------
def cancel(self):
self.close()
def addSelected(self):
self.liste = ["1", "2", "3", "4", "5"]
self.accept()
def exec_(self):
if QDialog.exec_(self) == QDialog.Accepted:
return self.liste
else:
return []
def test():
app = QApplication([""])
form = MyClass()
i = form.exec_()
print i
sys.exit(app.exec_())
#-------------------------------------------------------------------------------
# main
#-------------------------------------------------------------------------------
if __name__ == "__main__":
test()
To terminate a dialog, accept should work (at least if you've made your dialog modal, which I believe exec_ always does).
The normal alternative is reject; or, instead of either or both, you could call done with an int parameter (which becomes exec_'s result).
I don't know python at all but it looks like the dialog is the only window for your app. You may want to try invoking the dialog with form.show_() instead of form.exec_(). The latter is normally used to display the dialog modally over a parent window.