3

I am writing a small application using PyQT. I am trying to create a menu bar at the top of the application containing the usual options ("File", "Edit", "Options" etc.) but my toolbar is not appearing when I try to add it to my QMainWindow class. I have looked around but it is not obvious to me what I am doing wrong. Note that I have tried using the QMainWindow.menuBar() method instead of creating a QToolbar, but if I do that the bar remains invisible altogether. Using the code below, I at least get an empty bar, even if it is empty. The code below is a minimal example of this problem. I would like to know what I have to change for the actions to show up.

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QBrush
from PyQt5.QtWidgets import QMainWindow, QGraphicsScene, QToolBar, QMenu, QAction, QGraphicsView, QApplication


class GraphWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.scene = QGraphicsScene()

        # a grid foreground
        self.scene.setBackgroundBrush(QBrush(Qt.lightGray, Qt.CrossPattern))
        self.grid = True

        # Create upper toolbar with menu options
        tb = QToolBar()
        menu = QMenu()
        db_action = QAction("Open file")
        db_action.setStatusTip("Select a file to use as a database")
        db_action.triggered.connect(self.open_new_db)
        menu.addAction(db_action)

        tb.addWidget(menu)
        tb.setAllowedAreas(Qt.TopToolBarArea)
        tb.setFloatable(False)
        tb.setMovable(False)
        self.addToolBar(tb)

        self.statusBar().showMessage("Ready")

        # Demonstrate the results from the input.

        graphics = QGraphicsView(self.scene)
        self.setCentralWidget(graphics)
        self.showFullScreen()

    def open_new_db(self):
        pass

    def keyPressEvent(self, e):
        # Currently, we respond to a press of the Escape key by closing the program.
        if e.key() == Qt.Key_Escape:
            self.close()

app = QApplication(sys.argv)
gr = GraphWindow()
sys.exit(app.exec_())
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
Thalamus
  • 167
  • 2
  • 10
  • What OS version and QT version are you using? This [could](https://github.com/robotology/yarp/issues/521) be a [bug](https://bugs.launchpad.net/ubuntu/+source/appmenu-qt5/+bug/1307619) – Steve Lorimer Nov 20 '17 at 14:57
  • I use elementary OS 0.4.1 Loki and PyQT 5.9 – Thalamus Nov 20 '17 at 19:08

2 Answers2

3

To create a menu in the menu bar, you must give it a title. Also, Qt does not take ownership of the menus or actions, so you must either give them a parent or keep a reference to them in some other way, so that they don't get garbage-collected. Your example can be fixed like this:

class GraphWindow(QMainWindow):
    def __init__(self):
        ...
        # Create menu options
        menubar = self.menuBar()
        menu = QMenu('File', self) # title and parent
        db_action = QAction("Open file", self) # title and parent
        db_action.setStatusTip("Select a file to use as a database")
        db_action.triggered.connect(self.open_new_db)
        menu.addAction(db_action)
        menubar.addMenu(menu)

        self.statusBar().showMessage("Ready")

Note that the toolbar is not needed at all. A somewhat shorter and simpler way to achieve the same thing, is to add the menus and actions more directly, like this:

    menubar = self.menuBar()
    menu = menubar.addMenu('File')
    db_action = menu.addAction("Open file")
    db_action.setStatusTip("Select a file to use as a database")
    db_action.triggered.connect(self.open_new_db)

Here, Qt will automatically set the parents wherever required.

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
2

The problem is caused by the garbage collector, a variable only remains in the context that was created, and since you have created it in the constructor, the end of that python method eliminates it. Only the elements that have a parent or those that are members of the class remain so in this solution use the first option.

class GraphWindow(QMainWindow):
    def __init__(self):
        [...]
        # Create upper toolbar with menu options
        tb = QToolBar(self)
        menu = QMenu(self)
        db_action = QAction("Open file", self)
        db_action.setStatusTip("Select a file to use as a database")
        db_action.triggered.connect(self.open_new_db)
        menu.addAction(db_action)
        tb.addWidget(menu)
        tb.setAllowedAreas(Qt.TopToolBarArea)
        tb.setFloatable(False)
        tb.setMovable(False)
        self.addToolBar(tb)

        self.statusBar().showMessage("Ready")
        [...]
eyllanesc
  • 235,170
  • 19
  • 170
  • 241