Python: PyQt Popup Window

Morphine picture Morphine · Jan 29, 2011 · Viewed 70.8k times · Source

So I've been creating my GUI with Qt for my Python application. I've now come to a situation where after a button has been pushed the appropriate deferred gets executed, we perform some tasks then I need to open up a separate window that contains one or two things. But I can't seem to figure out how to create this new separate window. Could anyone give me an example of how to create one?

Answer

6502 picture 6502 · Jan 29, 2011

A common error that can drive you crazy is forgetting to store the handle of the popup window you create in some python variable that will remain alive (e.g. in a data member of the main window).

The following is a simple program that creates a main window with a button where pressing the button opens a popup

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import sys
from PyQt4.Qt import *

class MyPopup(QWidget):
    def __init__(self):
        QWidget.__init__(self)

    def paintEvent(self, e):
        dc = QPainter(self)
        dc.drawLine(0, 0, 100, 100)
        dc.drawLine(100, 0, 0, 100)

class MainWindow(QMainWindow):
    def __init__(self, *args):
        QMainWindow.__init__(self, *args)
        self.cw = QWidget(self)
        self.setCentralWidget(self.cw)
        self.btn1 = QPushButton("Click me", self.cw)
        self.btn1.setGeometry(QRect(0, 0, 100, 30))
        self.connect(self.btn1, SIGNAL("clicked()"), self.doit)
        self.w = None

    def doit(self):
        print "Opening a new popup window..."
        self.w = MyPopup()
        self.w.setGeometry(QRect(100, 100, 400, 200))
        self.w.show()

class App(QApplication):
    def __init__(self, *args):
        QApplication.__init__(self, *args)
        self.main = MainWindow()
        self.connect(self, SIGNAL("lastWindowClosed()"), self.byebye )
        self.main.show()

    def byebye( self ):
        self.exit(0)

def main(args):
    global app
    app = App(args)
    app.exec_()

if __name__ == "__main__":
    main(sys.argv)

What I think can be surprising for Python users and may be is the problem you are facing is the fact that if you don't store a reference to the new widget in the main e.g. by using w = MyPopup(...) instead of self.w = MyPopup(...) the window apparently doesn't appear (actually it's created and it's immediately destroyed).

The reason is that when the local variable w goes out of scope as no one is explicitly referencing the widget the widget gets deleted. This can be seen clearly because if you press again the button you'll see that as the second popup appears the first one is closed.

This also means that if you need to create several popups you have for example to put them in a python list and you should remove them from this list once the popups are closed by the user. The equivalent in the example could be changing to self.w = [] in constructor and then doing self.w.append(MyPopup(...)). Doing that would allow you to open several popups.