I'm working on a project where I have a Database linked with a Python interface (I'm using Qt Designer for the design). I want to have a delete button from my main window (QMainWindow) where, when I'm pressing it, it opens a pop up (QDialog) which says
Are you sure you want to delete this item?
But I have no idea how to do it.
Thanks for you help!
def button_click():
dialog = QtGui.QMessageBox.information(self, 'Delete?', 'Are you sure you want to delete this item?', buttons = QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel)
Bind this function to the button click event.
Let's say your Qt Designer ui has a main window called "MainWindow" and a button called "buttonDelete".
The first step is to set up your main window class and connect the button's clicked signal to a handler:
from PyQt4 import QtCore, QtGui
from mainwindow_ui import Ui_MainWindow
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setupUi(self)
self.buttonDelete.clicked.connect(self.handleButtonDelete)
Next you need to add a method to the MainWindow class that handles the signal and opens the dialog:
def handleButtonDelete(self):
answer = QtGui.QMessageBox.question(
self, 'Delete Item', 'Are you sure you want to delete this item?',
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No |
QtGui.QMessageBox.Cancel,
QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.Yes:
# code to delete the item
print('Yes')
elif answer == QtGui.QMessageBox.No:
# code to carry on without deleting
print('No')
else:
# code to abort the whole operation
print('Cancel')
This uses one of the built-in QMessageBox functions to create the dialog. The first three arguments set the parent, title and text. The next two arguments set the group of buttons that are shown, plus the default button (the one that is initially highlighted). If you want to use different buttons, the available ones can be found here.
To complete the example, you just need some code to start the application and show the window:
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
I got the same error for qt5.6
TypeError: question(QWidget, str, str, buttons: Union[QMessageBox.StandardButtons, QMessageBox.StandardButton] = QMessageBox.StandardButtons(QMessageBox.Yes|QMessageBox.No), defaultButton: QMessageBox.StandardButton = QMessageBox.NoButton): argument 1 has unexpected type 'Ui_MainWindow'
So I changed self to None in the following code, and it works.
def handleButtonDelete(self):
answer = QtGui.QMessageBox.question(
None, 'Delete Item', 'Are you sure you want to delete this item?',
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No |
QtGui.QMessageBox.Cancel,
QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.Yes:
# code to delete the item
print('Yes')
elif answer == QtGui.QMessageBox.No:
# code to carry on without deleting
print('No')
else:
# code to abort the whole operation
print('Cancel')
Related
OS: W10. This may be significant. If you have different results on a different platform, feedback would be helpful.
Here is an MRE. If you run it and go Ctrl+O, the menu labels become greyed. If you select a file in the QFileDialog by clicking the "Open" button or using its mnemonic (Alt+O), the open-file dialog is dismissed and the "Files" and "Help" menus become un-greyed.
However, if you go Ctrl+O again, and this time enter the name of a file in the "File name" box (QLineEdit), and then press Return, the dialog is dismissed (with a successful selection result) but the "Files" and "Help" menus remain greyed-out. It looks like this:
import sys, os
from PyQt5 import QtWidgets, QtCore, QtGui
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Greying of menus MRE')
self.setGeometry(QtCore.QRect(100, 100, 400, 200))
menubar = QtWidgets.QMenuBar(self)
self.setMenuBar(menubar)
self.files_menu = QtWidgets.QMenu('&Files', self)
menubar.addMenu(self.files_menu)
self.help_menu = QtWidgets.QMenu('&Help', self)
menubar.addMenu(self.help_menu)
self.new_action = QtWidgets.QAction('&New', self)
self.files_menu.addAction(self.new_action)
self.open_action = QtWidgets.QAction('&Open', self)
self.files_menu.addAction(self.open_action)
self.open_action.setShortcut("Ctrl+O")
self.open_action.triggered.connect(self.open_file)
def focusInEvent(self, event ):
print('main_window focusInEvent')
super().focusInEvent(event)
def focusOutEvent(self, event ):
print('main_window focusOutEvent')
super().focusInEvent(event)
def activateWindow(self):
print('main_window activateWindow')
super().activateWindow()
def open_file(self):
print('open file')
main_window_self = self
# open_doc_dialog = QtWidgets.QFileDialog(self.get_main_window())
class OpenDocFileDialog(QtWidgets.QFileDialog):
def accepted(self):
print('accepted')
super().accepted()
def accept(self):
print('accept')
super().accept()
def close(self):
print('close')
super().close()
def done(self, r):
print(f'done r {r}')
# neither of these solves the problem:
# main_window_self.activateWindow()
# main_window_self.files_menu.activateWindow()
super().done(r)
def hide(self):
print(f'hide')
super().hide()
def focusInEvent(self, event ):
print('focusInEvent')
super().focusInEvent(event)
def focusOutEvent(self, event ):
print('focusOutEvent')
super().focusInEvent(event)
def activateWindow(self):
print('activateWindow')
super().activateWindow()
open_doc_dialog = OpenDocFileDialog(self)
open_doc_dialog.setWindowTitle('Choose file')
open_doc_dialog.setDirectory(os.getcwd())
# we cannot use the native dialog, because we need control over the UI
options = open_doc_dialog.Options(open_doc_dialog.DontUseNativeDialog)
open_doc_dialog.setOptions(options)
open_doc_button = open_doc_dialog.findChild(QtWidgets.QDialogButtonBox).button(QtWidgets.QDialogButtonBox.Open)
lineEdit = open_doc_dialog.findChild(QtWidgets.QLineEdit)
# this does not solve the problem
# lineEdit.returnPressed.disconnect()
# lineEdit.returnPressed.connect(open_doc_button.click)
print(f'open_doc_button {open_doc_button}, lineEdit {lineEdit}')
# show the dialog
dialog_code = open_doc_dialog.exec()
if dialog_code != QtWidgets.QDialog.Accepted: return
sel_files = open_doc_dialog.selectedFiles()
print(f'sel_files: {sel_files}')
app = QtWidgets.QApplication([])
main_window = MainWindow()
main_window.show()
sys.exit(app.exec())
This problem can be understood, if not solved, with reference to this answer.
Note that this greying-out is not disablement. As explained in the above link, this has to do with "active/inactive states" of the menus (or their labels). The menus remain enabled throughout, although in this case it's impossible to know that while the open-file dialog is showing because it is modal. Clicking on one menu after the dialog has gone, or just hovering over it, is enough to un-grey them both...
The explanation, as I understand it, is that the "File name" box QLineEdit has a signal, returnPressed, which appears to activate something subtley different to the slot which is invoked when you use the "Choose" button. You can see I have experimented with trying to re-wire that signal, to no avail.
The method done of the QFileDialog appears to be called however the dialog closes (unlike close!), so I tried "activating" the main window... and then the individual QMenus... Doesn't work.
I am not clear how to get a handle on this "active state" business or why the slot connected to returnPressed is (seemingly) unable to give the "active state" back to the menus when the other slot manages to do so.
Edit
Searching on Musicamante's "unpolishing" suggestion led me to this:
lineEdit.returnPressed.disconnect()
def return_pressed():
style = main_window_self.menubar.style()
style.unpolish(main_window_self.menubar)
open_doc_button.click()
lineEdit.returnPressed.connect(return_pressed)
... unfortunately this doesn't work.
This looks like a possible Windows-related bug, since I can't reproduce it on Linux. As a work-around, you could try forcing a repaint after the dialog closes:
# show the dialog
dialog_code = open_doc_dialog.exec()
self.menubar.repaint()
Finally got it, thanks to Musicamante's suggestion:
lineEdit.returnPressed.disconnect()
def return_pressed():
style = main_window_self.menubar.style()
style.unpolish(main_window_self.menubar)
open_doc_button.click()
main_window_self.menubar.repaint()
lineEdit.returnPressed.connect(return_pressed)
... I actually tried this several times, just to make sure it was doing what was intended. So in fact, fortunately, no single-shot timer was needed in this case.
It's my code. How to store the lastly clicked/checked button as default in the next opening of the programme? For example: If we run the programme and click the 3rd button and close the entire programme. Whenever I reopen/re-run the programme, the lastly clicked button is checked by default ( ie. third button is checked by default)
import sys
from PyQt5 import QtWidgets
class RadioButton(QtWidgets.QWidget):
def __init__(self):
super(). __init__()
self.setWindowTitle("Radio Button")
self.rbtn1 = QtWidgets.QRadioButton("Button1")
self.rbtn1.clicked.connect(self.getvalue)
self.rbtn2 = QtWidgets.QRadioButton("Button2")
self.rbtn2.clicked.connect(self.getvalue)
self.rbtn3 = QtWidgets.QRadioButton("Button3")
self.rbtn3.clicked.connect(self.getvalue)
self.rbtn4 = QtWidgets.QRadioButton("Button4")
self.rbtn4.clicked.connect(self.getvalue)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(self.rbtn1)
vbox.addWidget(self.rbtn2)
vbox.addWidget(self.rbtn3)
vbox.addWidget(self.rbtn4)
self.setLayout(vbox)
def getvalue(self):
if self.rbtn1.isChecked():
self.rbtn1.setChecked(True)
print("Radio button 1 is checked")
elif self.rbtn2.isChecked():
self.rbtn2.setChecked(True)
print("Radio button 2 is checked")
elif self.rbtn3.isChecked():
self.rbtn3.setChecked(True)
print("Radio button 3 is checked")
elif self.rbtn4.isChecked():
self.rbtn4.setChecked(True)
print("Radio button 4 is checked")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
win = RadioButton()
win.show()
sys.exit(app.exec_())
You can use QSettings to save and restore data. Note that values are serialized as QVariants, so you cannot, for instance, save a custom python subclass instance.
In order to properly use QSettings you must set both the Qt application name and organization name (otherwise data won't be stored anywhere).
It is possible to save settings in other ways, but for general usage it's better to use the default behavior.
Note that, since values are stored as strings, Qt can only try to guess the actual type, so it's always better to specify the type when using value() (see the case below).
Then, while by default Qt is able to automatically group radio buttons and make them exclusive (see the autoExclusive property of QAbstractButton, which is set to True for QRadioButtons), for this kind of situations it's better to rely on a QButtonGroup, which not only allows better control on the buttons, but also allows identifying buttons member of the group by using unique IDs they were assigned to.
Finally, it's just a matter of connecting the appropriate signal of the button group to the function that saves the currently checked button.
from PyQt5 import QtWidgets, QtCore
class RadioButton(QtWidgets.QWidget):
def __init__(self):
super(). __init__()
self.setWindowTitle("Radio Button")
self.rbtn1 = QtWidgets.QRadioButton("Button1")
self.rbtn2 = QtWidgets.QRadioButton("Button2")
self.rbtn3 = QtWidgets.QRadioButton("Button3")
self.rbtn4 = QtWidgets.QRadioButton("Button4")
buttons = self.rbtn1, self.rbtn2, self.rbtn3, self.rbtn4
vbox = QtWidgets.QVBoxLayout(self)
self.optionGroup = QtWidgets.QButtonGroup()
for i, button in enumerate(buttons):
vbox.addWidget(button)
self.optionGroup.addButton(button, i)
# for Qt>=5.15:
# self.optionGroup.idToggled.connect(self.getvalue)
# otherwise:
self.optionGroup.buttonToggled[int, bool].connect(self.getvalue)
self.settings = QtCore.QSettings()
# read the value if it exists, otherwise use a default; note that
# the type MUST be specified, otherwise numbers would be read as
# strings, raising an exception and crashing the program in this case
default = self.settings.value('MyOption', 2, type=int)
self.optionGroup.button(default).setChecked(True)
def getvalue(self, id, checked):
if checked:
button = self.optionGroup.button(id)
print("{} is checked".format(button.text()))
self.settings.setValue('MyOption', id)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
app.setOrganizationName('MyOrganization')
app.setApplicationName('MyApplication')
win = RadioButton()
win.show()
sys.exit(app.exec_())
I strongly recommend you to carefully study the whole documentation about all the classes specified above.
On the right of the title bar of a PyQt QDialog (see below, next to the "x") there is a "?" that is supposed to help the user query help for any other widget on the Dialog window.
What should I do (programmatically) to get it to work. Once the "?" isClicked, one should be able to capture the next widget clicked and provide a ToolTip or something like that. In PyQt, I do not know how to capture the isClicked event on the "?".
I have seen a couple of posts where the question was how to make the "?" disappear, but the discussion there uses Qt, not PyQt, so I do not understand it, and they are not talking about what I need. I need to make it work as intended. See How can I hide/delete the "?" help button on the "title bar" of a Qt Dialog? and PyQt4 QInputDialog and QMessageBox window flags
You can set the whatsThis property to any widget you want:
self.someWidget.setWhatsThis('hello!')
From that point on, whenever you click on the "?" button and then click on that widget, a tooltip with that text will be shown.
Since the "what's this" mode is individually set to widgets, there's no easy way to capture it globally (as far as I know of) because if the widget has no whatsthis property set that feature won't be available for it.
Also, whenever you enter the "what's this" mode, the cursor will probably change according to the contents of the whatsthis property: if it's not set, the cursor will probably show a "disabled" icon.
I've created a basic workaround for this issue, which automatically enables any child widget's whatsthis (if none is already set) whenever the mode is activated: as soon as the EnterWhatsThisMode is fired, it automatically installs a custom object that acts as an event filter, and emits a signal if the whatsthis event is called; as soon as the mode exits, the filter is removed.
I used a separate object for the event filter because there's no way to know what event filter have been already installed to a widget, and if you already installed the parent's one, removing it automatically would be an issue.
class WhatsThisWatcher(QtCore.QObject):
whatsThisRequest = QtCore.pyqtSignal(QtWidgets.QWidget)
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.WhatsThis:
self.whatsThisRequest.emit(source)
return super(WhatsThisWatcher, self).eventFilter(source, event)
class W(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
layout = QtWidgets.QVBoxLayout(self)
hasWhatsThisButton = QtWidgets.QPushButton('Has whatsThis')
layout.addWidget(hasWhatsThisButton)
hasWhatsThisButton.setWhatsThis('I am a button!')
noWhatsThisButton = QtWidgets.QPushButton('No whatsThis')
layout.addWidget(noWhatsThisButton)
self.whatsThisWatchedWidgets = []
self.whatsThisWatcher = WhatsThisWatcher()
self.whatsThisWatcher.whatsThisRequest.connect(self.showCustomWhatsThis)
whatsThisButton = QtWidgets.QPushButton('Set "What\'s this" mode')
layout.addWidget(whatsThisButton)
whatsThisButton.clicked.connect(QtWidgets.QWhatsThis.enterWhatsThisMode)
def event(self, event):
if event.type() == QtCore.QEvent.EnterWhatsThisMode:
for widget in self.findChildren(QtWidgets.QWidget):
if not widget.whatsThis():
# install the custom filter
widget.installEventFilter(self.whatsThisWatcher)
# set an arbitrary string to ensure that the "whatsThis" is
# enabled and the cursor is correctly set
widget.setWhatsThis('whatever')
self.whatsThisWatchedWidgets.append(widget)
elif event.type() == QtCore.QEvent.LeaveWhatsThisMode:
while self.whatsThisWatchedWidgets:
widget = self.whatsThisWatchedWidgets.pop()
# reset the whatsThis string to none and uninstall the filter
widget.setWhatsThis('')
widget.removeEventFilter(self.whatsThisWatcher)
return super(W, self).event(event)
def showCustomWhatsThis(self, widget):
widgetPos = widget.mapTo(self, QtCore.QPoint())
QtWidgets.QWhatsThis.showText(
QtGui.QCursor.pos(),
'There is no "what\'s this" for {} widget at coords {}, {}'.format(
widget.__class__.__name__, widgetPos.x(), widgetPos.y()),
widget)
A couple of notes about this:
I used a button to activate the whatsthis mode, as on my window manager on Linux there's no window title button for that;
Some widgets may contain subwidgets, and you'll get those instead of the "main" one (the most common case are QAbstractScrollArea descendands, like QTextEdit or QGraphicsView, which might return the viewport, the inner "widget" or the scrollbars);
By default the task of that button is to enable whatsThis: press "?", then press the widget and you will see the message associated with whatsThis property.
If you want to add other actions(open url, add QToolTip, etc) you can monitor the QEvent::EnterWhatsThisMode and QEvent::LeaveWhatsThisMode events overriding the event() method or using an eventFilter().
from PyQt5 import QtCore, QtGui, QtWidgets
class Dialog(QtWidgets.QDialog):
def event(self, event):
if event.type() == QtCore.QEvent.EnterWhatsThisMode:
print("enter")
QtGui.QDesktopServices.openUrl(QtCore.QUrl("foo_url"))
elif event.type() == QtCore.QEvent.LeaveWhatsThisMode:
print("leave")
return super().event(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Dialog()
w.setWhatsThis("Whats this")
w.setWindowFlags(
QtCore.Qt.WindowContextHelpButtonHint | QtCore.Qt.WindowCloseButtonHint
)
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
I'm a newbie to Python and PyQt. I've tried to manage closeEvent to ask before to close mainwindow, but this work well only from 'X' button. From the QMEssageBox created to ask the user, callEvent() it's called two times.
This is the relevant part of the code :
self.ui.actionChiudi.triggered.connect(self.close)
def closeEvent(self, event):
#check presence of data in the table
if self.csvViewer.rowCount() > 0:
print event # only to analyze the caller
#show a warning
Error = QtGui.QMessageBox()
Error.setIcon(QtGui.QMessageBox.Question)
Error.setWindowTitle('ATTENZIONE !!')
Error.setInformativeText(u"Sei sicuro di voler uscire?")
Error.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
ret = Error.exec_()
if ret == QtGui.QMessageBox.Ok:
event.accept()
else:
event.ignore()
else:
#close directly
event.accept()
'actionChiudi' is the menù item in the main menù.
For what I can understand, when use 'X' button, the function close() it's called only one time directly from mainwindow object, then close my app.
When use the menù item, the function create the new object 'QMessageBox' then call 'closeEvent()' one time for this object, then recall the same function for the mainwindow object. If this is correct, I don't know how to manage this.
Thanks in advance for any help!
thankyou for your tips about howto construct a good example of code. Sorry but I'm new there, then I did not succeed in making a good example. But, in the meantime, I was thinking about the problem and, at the end, I've solved this with a little trick.
I've added a global variable that count if you are closing the main window, then at the other calls to closeEvent() avoid to recall the test procedure.
I've tried to create a class for the generation of QMessageBox external to main class, then to override closeEvent() of this object, but don't work. The only way that I've found is with global variable. The program continue to call two times closeEvent (one for the QMessageBox and one for the mainwindow) but now, the second time ignore to recall the test. It's a trick, it's not elegant, but it works for me.
The piece of code now is this :
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sys
import os
import datetime
import inspect
import csv
import codecs
import pymssql
from PyQt4 import QtGui, QtCore, Qt
import mainwindow
#import _winreg only on Microsoft platforms
import platform
winos = True
if os.name == 'nt' and platform.system() == 'Windows':
import _winreg
#other stuff needed under Windows
import _mssql
import decimal
import uuid
winos = True
else:
winos = False
#other global variables
liccode = False
closemain = False
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
super(MainWindow, self).__init__(parent)
self.ui = mainwindow.Ui_MainWindow()
self.ui.setupUi(self)
# some stuff and widgets to visualize in the main window
#connettors to menù functions; leave only the closing one
self.ui.actionChiudi.triggered.connect(self.close)
#override of closeEvent - there I think, it's better to work to separate
#closeEvent for mainwindow from general closeEvent. But how?
def closeEvent(self, event):
global closemain
#make evaluation test to decide how to work. Close directly or no?
if self.csvViewer.rowCount() > 0 and not closemain:
print event
#show a warning
Error = QtGui.QMessageBox()
Error.setIcon(QtGui.QMessageBox.Question)
Error.setWindowTitle('WARNING !!')
Error.setInformativeText(u"Are you sure to leave?")
Error.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
ret = Error.exec_()
if ret == QtGui.QMessageBox.Ok:
closemain = True
event.accept()
else:
event.ignore()
else:
#close directly
event.accept()
#start application and create main
app = QtGui.QApplication(sys.argv)
my_mainWindow = MainWindow()
my_mainWindow.show()
sys.exit(app.exec_())
In any case, any advice is well accepted to make a better code. Thank you all!
I'm working though the pyqt5 tutorial found here Zetcode, PyQt5
As an exercise for myself I'm trying to expand on an example so that I am presented with the same dialog message box regardless of method used to close the app:
clicking the 'X' button in the title bar (works as intended)
clicking the 'Close' button (produces attribute error)
pressing the 'escape' key (works but not sure how/why)
The dialog message box is implemented in the closeEvent method, full script provided at the end.
I'm having two issues:
1. When clicking 'Close' button, instead of just quitting, I want to call closeEvent method including message box dialog.
I have replaced a line of the example code for the 'Close' push button:
btn.clicked.connect(QCoreApplication.instance().quit)
And instead am trying to call the closeEvent method which already implements the dialog I want:
btn.clicked.connect(self.closeEvent)
However when i run the script and click the 'Close' button and select the resulting 'Close' option in the dialog i get the following:
Traceback (most recent call last):
File "5-terminator.py", line 41, in closeEvent
event.accept()
AttributeError: 'bool' object has no attribute 'accept'
Aborted
Can anyone advise what I'm doing wrong and what needs to be done here?
2. When hitting the escape key somehow the message box dialog is presented and works just fine.
Ok, it's great that it works, but I'd like to know how and why the message box functionality defined in CloseEvent method is called within the keyPressEvent method.
Full script follows:
import sys
from PyQt5.QtWidgets import (
QApplication, QWidget, QToolTip, QPushButton, QMessageBox)
from PyQt5.QtCore import QCoreApplication, Qt
class Window(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
btn = QPushButton("Close", self)
btn.setToolTip("Close Application")
# btn.clicked.connect(QCoreApplication.instance().quit)
# instead of above button signal, try to call closeEvent method below
btn.clicked.connect(self.closeEvent)
btn.resize(btn.sizeHint())
btn.move(410, 118)
self.setGeometry(30, 450, 500, 150)
self.setWindowTitle("Terminator")
self.show()
def closeEvent(self, event):
"""Generate 'question' dialog on clicking 'X' button in title bar.
Reimplement the closeEvent() event handler to include a 'Question'
dialog with options on how to proceed - Save, Close, Cancel buttons
"""
reply = QMessageBox.question(
self, "Message",
"Are you sure you want to quit? Any unsaved work will be lost.",
QMessageBox.Save | QMessageBox.Close | QMessageBox.Cancel,
QMessageBox.Save)
if reply == QMessageBox.Close:
event.accept()
else:
event.ignore()
def keyPressEvent(self, event):
"""Close application from escape key.
results in QMessageBox dialog from closeEvent, good but how/why?
"""
if event.key() == Qt.Key_Escape:
self.close()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
sys.exit(app.exec_())
Hope someone can take the time to enlighten me.
Your second question answers the first question.
The reimplemented keyPressEvent method calls close(), which sends a QCloseEvent to the widget. Subsequently, the widget's closeEvent will be called with that event as its argument.
So you just need to connect the button to the widget's close() slot, and everything will work as expected:
btn.clicked.connect(self.close)
Unlike the X button your custom button does not seem to pass an close event just a bool. That's why this exercise should work for the X button but not a normal button. In any case, for your first question you might use destroy() and pass instead (of accept and ignore) just like this:
import sys
from PyQt5.QtWidgets import (
QApplication, QWidget, QToolTip, QPushButton, QMessageBox)
from PyQt5.QtCore import QCoreApplication, Qt
class Window(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
btn = QPushButton("Close", self)
btn.setToolTip("Close Application")
# btn.clicked.connect(QCoreApplication.instance().quit)
# instead of above button signal, try to call closeEvent method below
btn.clicked.connect(self.closeEvent)
btn.resize(btn.sizeHint())
btn.move(410, 118)
self.setGeometry(30, 450, 500, 150)
self.setWindowTitle("Terminator")
self.show()
def closeEvent(self, event):
"""Generate 'question' dialog on clicking 'X' button in title bar.
Reimplement the closeEvent() event handler to include a 'Question'
dialog with options on how to proceed - Save, Close, Cancel buttons
"""
reply = QMessageBox.question(
self, "Message",
"Are you sure you want to quit? Any unsaved work will be lost.",
QMessageBox.Save | QMessageBox.Close | QMessageBox.Cancel,
QMessageBox.Save)
if reply == QMessageBox.Close:
app.quit()
else:
pass
def keyPressEvent(self, event):
"""Close application from escape key.
results in QMessageBox dialog from closeEvent, good but how/why?
"""
if event.key() == Qt.Key_Escape:
self.close()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
sys.exit(app.exec_())
For your second question Qt has default behaviors depending on the Widget (Dialogs might have another, try pressing the Esc key when your Message Dialog is open just to see). When you do need to override the Esc behavior you might try this:
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
print("esc")
As you'll eventually see in ZetCode.
closeEvent()
In above right tick mark code please check the closeEvent it is same other wise it's waste of time to research.