0

I'm using Python 3 and PyQt4

I'm trying to make a simple main window with a menubar. It doesn't work if I try to set up the menubar in the MainWindow initialization but does work if I set it up in some external function. That is, the following does NOT work:

import sys
from PyQt4 import QtGui

class MyMainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        centralWidget = QtGui.QWidget()
        menubar = QtGui.QMenuBar()
        menu = QtGui.QMenu("File")
        menu.addAction("New")
        menubar.addMenu(menu)
        self.setMenuBar(menubar)
        self.setCentralWidget(centralWidget)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    mainWindow = MyMainWindow()
    mainWindow.show()
    sys.exit(app.exec_())    

While if I simply move the menu setup down to the main routine:

class MyMainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    mainWindow = MyMainWindow()

    centralWidget = QtGui.QWidget()
    menubar = QtGui.QMenuBar()
    menu = QtGui.QMenu("File")
    menu.addAction("New")
    menubar.addMenu(menu)
    mainWindow.setMenuBar(menubar)
    mainWindow.setCentralWidget(centralWidget)

    mainWindow.show()
    sys.exit(app.exec_()) 

Then the menu is created (I know I don't have any actions hooked up, this is stripped down to show the oddity). I thought it might have to do with being in init but moving it to another class routine, e.g. setup(self), and then calling that after creating the mainWindow doesn't solve the problem.

I seem to be continually baffled by what works and what doesn't in PyQt4. If anyone could point me at some good resources I'd also appreciate it (I've read Rapid GUI Programming but find I'm still lost).

Thanks in advance for your help

ehudson
  • 205
  • 3
  • 6
  • Of course, as soon as I ask I think I've figured it out. Does it have something to do with garbage collection? If I use "self.menu" instead of "menu," then the menu works. So even though the menu is added to the menubar it doesn't stick around? Is there some standard way of keeping the menu alive? – ehudson May 22 '15 at 17:08
  • There's no need to use `QMenuBar/setMenuBar` - just do `menubar = self.menuBar()`. – ekhumoro May 22 '15 at 17:28

1 Answers1

0

PyQt can be confusing because there are two object ownership models in use together. Here for example you are creating a QMenu which can be owned either by your Python program or by the Qt object hierarchy.

QMenus have two typical use cases: in one you create a pop-up menu and use QMenu.exec_ to await a response modally. In this case you naturally "own" the menu: you will hold a reference to it until it has finished and closed.

But your code illustrates the other case. Here you have no interest in holding a reference to the object: you have added one just as a workaround. But you don't need to do this.

Qt has its own object ownership hierarchy which typically starts at the top-level windows which have no parent and then goes down through all their component widgets. This parenting is typically established by assigning the parent upon creation of each child QObject. Once this is done, the object's lifetime is tied to that of their parent unless they are explicitly deleted sooner.

So that's what you want in this case:

menu = QtGui.QMenu("File",self)

The other widgets such as the central widget and the menu bar could have the same issue. But in each case they are being assigned a parent as they are being added to the main window.

Giving QObjects parents is generally the right thing to do. It soon becomes second nature to just add a parent parameter to every creation call.

strubbly
  • 3,347
  • 3
  • 24
  • 36