14

I'm a total newbie in PyQt trying to develop simple application. I have designed simple ui with Qt-designer. I want extra confirmation if the user really want to exit application when clicking X or ,,Exit'' button or choosing Exit from menu.

Here's the code:

import sys
from PyQt4 import QtGui, QtCore, uic

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)

        self.ui = uic.loadUi('main_window.ui')
        self.ui.show()

        self.ui.btnExit.clicked.connect(self.close)
        self.ui.actionExit.triggered.connect(self.close)

    def closeEvent(self, event):
        print("event")
        reply = QtGui.QMessageBox.question(self, 'Message',
            "Are you sure to quit?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()


def main():
    app = QtGui.QApplication(sys.argv)
    win = MainWindow()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

The problem is that:

  • When I click X on main window the closeEvent function doesn't trigger
  • When I click Exit button or choose ,,Exit'' from menu, the function
    is called, but clicking Yes doesn't close application.

I have found some questions on SO and searched for tutorials, but nothing covered such problem. What am I doing wrong?

Wookie88
  • 33,079
  • 4
  • 27
  • 32

4 Answers4

19

Note that you're doing:

self.ui = uic.loadUi('main_window.ui')
self.ui.show()

Your actual window is an instance attribute (ui) inside win. Not the win itself. And it doesn't have closeEvent implemented.

loadUi can load the .ui file inside an instance.

PyQt4.uic.loadUi(uifile[, baseinstance=None[, package='']])

You should use that. With that, your code would be:

import sys
from PyQt4 import QtGui, QtCore, uic

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)

        uic.loadUi('main_window.ui', self)

        self.btnExit.clicked.connect(self.close)
        self.actionExit.triggered.connect(self.close)

    def closeEvent(self, event):
        print("event")
        reply = QtGui.QMessageBox.question(self, 'Message',
            "Are you sure to quit?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()


def main():
    app = QtGui.QApplication(sys.argv)
    win = MainWindow()
    win.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

Note: I'm not a fan of showing the window in __init__. Explicit is better. So, I moved that to main. Feel free to modify it.

Avaris
  • 35,883
  • 7
  • 81
  • 72
  • 1
    Thank you very much, that solved both of my problems. I know I was missing something quite simple, but tutorials which I had found didn't cover that topic. Explicit show is also a good solution, thanks for pointing that out. Btw: can you recommend any good sources for learning PyQt4? – Wookie88 Feb 12 '13 at 15:46
  • 1
    @Wookie88: I don't know much tutorials. [This one](http://zetcode.com/tutorials/pyqt4/) looks fine. Also, official Qt docs have usage examples all around. And PyQt ships with a good selection of examples. – Avaris Feb 12 '13 at 23:48
16

it works for me, just adding this line

self.ui.closeEvent = self.closeEvent

so your code would be:

import sys
from PyQt4 import QtGui, QtCore, uic

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)

        self.ui = uic.loadUi('main_window.ui')
        self.ui.closeEvent = self.closeEvent
        self.ui.show()

        self.ui.btnExit.clicked.connect(self.close)
        self.ui.actionExit.triggered.connect(self.close)

    def closeEvent(self, event):
        print("event")
        reply = QtGui.QMessageBox.question(self, 'Message',
            "Are you sure to quit?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()


def main():
    app = QtGui.QApplication(sys.argv)
    win = MainWindow()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
Anggun Pribadi
  • 161
  • 1
  • 3
2

Another simple solution for this is using the app.aboutToQuit.connect(self.close_event) to run the code in the closeEvent function whenever the user clicks the close button.

Sample code here

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

#--------------------------------------------------------------------------------

        app.aboutToQuit.connect(self.closeEvent) #this line is what ur looking for !!!!!!

#--------------------------------------------------------------------------------

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle('Demo')

    #{______________________________________

    def closeEvent(self):
        #Your code here
        print('User has pressed the close button')
        import sys
        sys.exit(0)

    #}______________________________________


if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    MainWindow = QtGui.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
Natesh bhat
  • 12,274
  • 10
  • 84
  • 125
0

From the designer you can connect events to the main window, and add new slots to it in the designer, then just implement the methods in python and the event->slot connection will be done automatically.

LtWorf
  • 7,286
  • 6
  • 31
  • 45