• How menus can be used in a main window application
  • Menu widget can be either
    • a pull-down menu in a menu bar or
      • Pull-down menus are shown by the menu bar when the user clicks on the respective item or presses the specified shortcut key.
    • a standalone context menu.
      • Context menus are usually invoked by some special keyboard key or by right-clicking.

menu_example

  • A menu consists of a list of action items. -In applications, many common commands can be invoked via menus, toolbar buttons as well as keyboard shortcuts.
    • Since the user expects the commands to be performed in the same way, regardless of the user interface used,
      • it is useful to represent each command as an action.
  • The Menus example consists of one single class, MainWidow, derived from the QMainWindow class.
    • When choosing one of the action items in our application, it will display the item’s path in its central widget.

MainWindow Class Definition

  • QMainWindow class provides a main application window, with a menu bar, tool bars, dock widgets and a status bar around a large central widget.
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow();

protected:
#ifndef QT_NO_CONTEXTMENU
    void contextMenuEvent(QContextMenuEvent *event) override;
#endif // QT_NO_CONTEXTMENU
  • In this example, we will see how to implement pull-down menus as well as a context menu.
    • In order to implement a custom context menu we must reimplement QWidget’s contextMenuEvent() function to receive the context menu events for our main window.
private slots:
    void newFile();
    void open();
    void save();
    void print();
    void undo();
    void redo();
    void cut();
    void copy();
    void paste();
    void bold();
    void italic();
    void leftAlign();
    void rightAlign();
    void justify();
    void center();
    void setLineSpacing();
    void setParagraphSpacing();
    void about();
    void aboutQt();
  • We must also implement a collection of private slots to respond to the user activating any of our menu entries.
    • Note that these slots are left out of this documentation since they are trivial,
    • i.e. most of them are only displaying the actions’s path in the main window’s central widget.
private:
    void createActions();
    void createMenus();
  • simplify the constructor by implementing two private convenience functions
    • to create the various actions,
    • to add them to menus
    • and to insert the menus into our main window’s menu bar.
    QMenu* fileMenu_;
    QMenu* editMenu_;
    QMenu* formatMenu_;
    QMenu* helpMenu_;
    QActionGroup* alignmentGroup_;
    QAction* newAct_;
    QAction* openAct_;
    QAction* saveAct_;
    QAction* printAct_;
    QAction* exitAct_;
    QAction* undoAct_;
    QAction* redoAct_;
    QAction* cutAct_;
    QAction* copyAct_;
    QAction* pasteAct_;
    QAction* boldAct_;
    QAction* italicAct_;
    QAction* leftAlignAct_;
    QAction* rightAlignAct_;
    QAction* justifyAct_;
    QAction* centerAct_;
    QAction* setLineSpacingAct_;
    QAction* setParagraphSpacingAct_;
    QAction* aboutAct_;
    QAction* aboutQtAct_;
    QLabel* infoLabel_;
  • Finally, we declare the various menus and actions as well as a simple information label in the application wide scope.
  • The QMenu class provides a menu widget for use in
    • menu bars,
    • context menus,
    • and other popup menus
  • while the QAction class provides an abstract UI action that can be inserted into widgets.
  • In some situations it is useful to group actions together,
    • e.g., we have a Left Align actions, a Right Align action, a Justify action, and a Center action, and we want only one of these actions to be active at any one time.
    • One simple way of achieving this is to group the actions together in an action group using the QActionGroup class.

MainWindow Class Implementation

  • In the constructor, we start off by creating a regular QWidget and make it our main window’s central widget.
    • Note that the main window takes ownership of the widget pointer and deletes it at the appropriate time.
    MainWindow::MainWindow()
    {
      QWidget* widget = new QWidget;
      setCentralWidget(widget);
    
      QWidget* topFiller = new QWidget;
      topFiller->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    
      infoLabel_ = new QLabel(tr("<i>Choose a menu option, or right-click to "
                                "invoke a context menu</i>"));
      infoLabel_->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
      infoLabel_->setAlignment(Qt::AlignCenter);
    
      QWidget* bottomFiller = new QWidget;
      bottomFiller->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    
      QVBoxLayout* layout = new QVBoxLayout;
      layout->setContentsMargins(5, 5, 5, 5);
      layout->addWidget(topFiller);
      layout->addWidget(infoLabel_);
      layout->addWidget(bottomFiller);
      widget->setLayout(layout);
    
  • Then we create the information label as well as a top and bottom filler that we add to a layout which we install on the central widget.
  • QMainWindow objects come with their own customized layout
    • Followings are considered as errors.
      • setting a layout on a the actual main window,
      • or creating a layout with a main window as a parent
    • You should always set your own layout on the central widget instead.
    createActions();
    createMenus();

    QString message = tr("A context menu is available by right-clicking");
    statusBar()->showMessage(message);

    setWindowTitle("Menus Example");
    setMinimumSize(160, 160);
    resize(480, 320);
}
  • QMainWindow’s statusBar(){target=”_blank”} function returns the status bar for the main window (if the status bar does not exist, this function will create and return an empty status bar).
    • We initialize the status bar and window title, resize the window to an appropriate size as well as ensure that the main window cannot be resized to a smaller size than the given one.
void MainWindow::createActions()
{
    newAct_ = new QAction(tr("&New"), this);
    newAct_->setShortcuts(QKeySequence::New);
    newAct_->setStatusTip(tr("Create a new file"));
    connect(newAct_, &QAction::triggered, this, &MainWindow::newFile);
    ...
    ...
  • A QAction object may contain
    • an icon,
    • a text,
    • a shortcut,
    • a status tip,
    • a “What’s this?” text,
    • and a tooltip.
  • Most of these can be set in the constructor, but they can also be set independently using the provided convenience functions.

  • In the createActions() function,
    • we first create a newAct action.
      • We make Ctrl+N its shortcut using the QAction::setShortcut() function,
      • and we set its status tip using QAction::setStatusTip() function (the status tip is displayed on all status bars provided by the actions’s top-level parent widget).
      • We also connect its triggered() signal to the newFile() slot.
  • The rest of the actions are created in a similar manner.
    alignmentGroup_ = new QActionGroup(this);
    alignmentGroup_->addAction(leftAlignAct_);
    alignmentGroup_->addAction(rightAlignAct_);
    alignmentGroup_->addAction(justifyAct_);
    alignmentGroup_->addAction(centerAct_);
    leftAlignAct_->setCheckable(true);
}
  • Once we have created the Left Align, Right Align, Justify, and a Center actions, we can also create the previously mentioned action group.

  • Each action is added to the group using QActionGroup’s addAction() function.
    • Note that an action also can be added to a group by creating it with the group as its parent.
      • Since an action group is exclusive by default, only one of the actions in the group is checked at any one time
  • When all the actions are created, we use the createMenus() function to add the actions to the menus and to insert the menus into the menu bar:
void MainWindow::createMenus()
{
    fileMenu_ = menuBar()->addMenu(tr("&File"));
    fileMenu_->addAction(newAct_);
    fileMenu_->addAction(openAct_);
    fileMenu_->addAction(saveAct_);
    fileMenu_->addAction(printAct_);
    fileMenu_->addSeparator();
    fileMenu_->addAction(exitAct_);

    editMenu_ = menuBar()->addMenu(tr("&Edit"));
    editMenu_->addAction(undoAct_);
    editMenu_->addAction(redoAct_);
    editMenu_->addSeparator();
    editMenu_->addAction(cutAct_);
    editMenu_->addAction(copyAct_);
    editMenu_->addAction(pasteAct_);
    editMenu_->addSeparator();
  • QMenuBar’s addMenu() function appends a new QMenu with the given title, to the menu bar
    • (note that the menu bar takes ownership of the menu).
    • We use QWidget’s addAction() function to add each action to the corresponding menu.
      • Alternatively, the QMenu class provides several addAction() convenience functions that create and add new actions from given texts and/or icons.
        • You can also provide a member that will automatically connect to
  • The QMenu::addSeparator() function creates and returns a new separator action,
    • i.e. an action for which QMenu::isSeparator() returns true,
    • and adds the new action to the menu’s list of actions.
    formatMenu_ = editMenu_->addMenu(tr("&Format"));
    formatMenu_->addAction(boldAct_);
    formatMenu_->addAction(italicAct_);
    formatMenu_->addSeparator()->setText(tr("Alignment"));
    formatMenu_->addAction(leftAlignAct_);
    formatMenu_->addAction(rightAlignAct_);
    formatMenu_->addAction(justifyAct_);
    formatMenu_->addAction(centerAct_);
    formatMenu_->addSeparator();
    formatMenu_->addAction(setLineSpacingAct_);
    formatMenu_->addAction(setParagraphSpacingAct_);
  • Note the Format menu
    • It is added as a submenu to the Edit Menu using QMenu’s addMenu function.
    • Take a look at the alignment actions: In the createActions() function we added the leftAlignAct, rightAlignAct, justifyAct and centerAct actions to an action group.
      • Nevertheless, we must add each action to the menu separately while the action group does its magic behind the scene.
#ifndef QT_NO_CONTEXTMENU
void MainWindow::contextMenuEvent(QContextMenuEvent* event)
{
    QMenu menu(this);
    menu.addAction(cutAct_);
    menu.addAction(copyAct_);
    menu.addAction(pasteAct_);
    menu.exec(event->globalPos());
}
#endif	// QT_NO_CONTEXTMENU
  • To provide a custom context menu, we must reimplement QWidget’s contextMenuEvent() function to receive the widget’s context menu events.
  • Whenever we receive such an event, we create a menu containing the Cut, Copy and Paste actions,
    • Context menus can be executed either asynchronously using the popup() function or synchronously using the exec() function.
  • By passing the event’s position as argument, we ensure that the context menu appears at the expected position.