Implementing Main Windows and Menu Bars in Qt
Concept of the Main Window
In graphical user interface development, the Main Window acts as the primary entry point for user interaction. Unlike transient dialog boxes, the main window persists for the duration of the application's lifecycle. It typically encapsulates the core functionality of the software and serves as the parent container for various other interface elements. Structural, an application is often composed of a single main window that manages multiple child dialogs or sub-windows.
Qt's QMainWindow
The Qt framework provides the QMainWindow class specifically to facilitate this design pattern. QMainWindow inherits from QWidget and functions as a specialized container. It comes with a pre-configured layout that includes specific areas for a menu bar, toolbars, dock widgets, a status bar, and a central widget. This layout management simplifies the development of standard application structures by handling the geometry of these common elements automatically.
Building the Menu System
Menus in Qt are constructed using three primary components: QMenuBar, QMenu, and QAction.
- QMenuBar: The horizontal bar located at the top of the window (or on macOS, at the top of the screen) that holds the main menu titles.
- QMenu: The popup list containing specific commands, such as "File" or "Edit".
- QAction: The abstract representation of a user command. An action can appear in a menu, a toolbar, or both, and it holds the logic for the command, including its text, icon, and keyboard shortcut.
The implementation logic generally follows a hierarcihcal approach: first, obtain or create the menu bar; second, create and attach specific menus to the bar; and third, populate those menus with actions.
Code Implementation
The following example demonstrates how to create a QMainWindow subclass and programmatical populate its menu bar with standard options.
Header File (AppWindow.h)
#ifndef APPWINDOW_H
#define APPWINDOW_H
#include <QMainWindow>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QString>
class AppWindow : public QMainWindow
{
Q_OBJECT
public:
explicit AppWindow(QWidget *parent = nullptr);
~AppWindow();
private:
bool setupUserInterface();
bool setupMenuBar();
// Menu initialization helpers
bool createFileMenu(QMenuBar* bar);
bool createEditMenu(QMenuBar* bar);
bool createViewMenu(QMenuBar* bar);
bool createHelpMenu(QMenuBar* bar);
// Helper to generate actions
QAction* generateAction(const QString& text, const QKeySequence& shortcut);
};
#endif // APPWINDOW_H
Source File (AppWindow.cpp)
#include "AppWindow.h"
AppWindow::AppWindow(QWidget *parent)
: QMainWindow(parent)
{
setupUserInterface();
}
AppWindow::~AppWindow()
{
}
bool AppWindow::setupUserInterface()
{
bool initializationSuccess = true;
initializationSuccess = setupMenuBar();
return initializationSuccess;
}
bool AppWindow::setupMenuBar()
{
bool success = true;
QMenuBar* mainMenuBar = menuBar();
if (mainMenuBar != nullptr) {
success = success && createFileMenu(mainMenuBar);
success = success && createEditMenu(mainMenuBar);
success = success && createViewMenu(mainMenuBar);
success = success && createHelpMenu(mainMenuBar);
} else {
success = false;
}
return success;
}
bool AppWindow::createFileMenu(QMenuBar* bar)
{
QMenu* fileMenu = new QMenu("File(&F)", this);
bool success = (fileMenu != nullptr);
if (success) {
QAction* action = nullptr;
// Create New Action
action = generateAction("New(&N)", QKeySequence(Qt::CTRL | Qt::Key_N));
fileMenu->addAction(action);
fileMenu->addSeparator();
// Create Open Action
action = generateAction("Open(&O)...", QKeySequence(Qt::CTRL | Qt::Key_O));
fileMenu->addAction(action);
fileMenu->addSeparator();
// Create Save Action
action = generateAction("Save(&S)", QKeySequence(Qt::CTRL | Qt::Key_S));
fileMenu->addAction(action);
fileMenu->addSeparator();
// Create Exit Action
action = generateAction("Exit(&X)", QKeySequence());
fileMenu->addAction(action);
bar->addMenu(fileMenu);
}
return success;
}
bool AppWindow::createEditMenu(QMenuBar* bar)
{
QMenu* editMenu = new QMenu("Edit(&E)", this);
bool success = (editMenu != nullptr);
if (success) {
QAction* action = nullptr;
// Undo
action = generateAction("Undo(&U)", QKeySequence(Qt::CTRL | Qt::Key_Z));
editMenu->addAction(action);
editMenu->addSeparator();
// Redo
action = generateAction("Redo(&R)", QKeySequence(Qt::CTRL | Qt::Key_Y));
editMenu->addAction(action);
editMenu->addSeparator();
// Cut
action = generateAction("Cut(&T)", QKeySequence(Qt::CTRL | Qt::Key_X));
editMenu->addAction(action);
editMenu->addSeparator();
// Copy
action = generateAction("Copy(&C)", QKeySequence(Qt::CTRL | Qt::Key_C));
editMenu->addAction(action);
editMenu->addSeparator();
// Paste
action = generateAction("Paste(&P)", QKeySequence(Qt::CTRL | Qt::Key_V));
editMenu->addAction(action);
bar->addMenu(editMenu);
}
return success;
}
bool AppWindow::createViewMenu(QMenuBar* bar)
{
QMenu* viewMenu = new QMenu("View(&V)", this);
bool success = (viewMenu != nullptr);
if (success) {
QAction* action = nullptr;
action = generateAction("Tool Bar(&T)", QKeySequence());
viewMenu->addAction(action);
viewMenu->addSeparator();
action = generateAction("Status Bar(&S)", QKeySequence());
viewMenu->addAction(action);
bar->addMenu(viewMenu);
}
return success;
}
bool AppWindow::createHelpMenu(QMenuBar* bar)
{
QMenu* helpMenu = new QMenu("Help(&H)", this);
bool success = (helpMenu != nullptr);
if (success) {
QAction* action = nullptr;
action = generateAction("User Manual", QKeySequence());
helpMenu->addAction(action);
helpMenu->addSeparator();
action = generateAction("About App", QKeySequence());
helpMenu->addAction(action);
bar->addMenu(helpMenu);
}
return success;
}
QAction* AppWindow::generateAction(const QString& text, const QKeySequence& shortcut)
{
QAction* action = new QAction(text, this);
if (action != nullptr) {
if (!shortcut.isEmpty()) {
action->setShortcut(shortcut);
}
}
return action;
}
Main Entry Point (main.cpp)
#include <QApplication>
#include "AppWindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
AppWindow window;
window.show();
return app.exec();
}