Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Building Application Main Windows with Qt QMainWindow

Tech 1

QMainWindow is Qt's primary class for creating application main windows. It inherits from QWidget and ships with a pre-built layout structure, including a single menu bar, one or more toolbars, dockable floating widgets, a status bar, and a central content widget. It serves as the foundation for most desktop Qt applications, including text editors, image viewers, and IDEs.

Menu Bar

Qt implements menu bars with the QMenuBar class. A main window can only have one menu bar, located at the top of the window just below the title bar. Menu bars contain top-level menus, which in turn contain clickable action items.

1. Create Menu Bar and Add Menus

You can create a menu bar manually and attach it to the main window, or use the built-in menuBar() method provided by QMainWindow. Add top-level menus with QMenuBar::addMenu():

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // Create custom menu bar
    QMenuBar* mainMenuBar = new QMenuBar(this);
    this->setMenuBar(mainMenuBar);

    // Add top-level menus
    QMenu* fileMenu = new QMenu(tr("File"));
    QMenu* editMenu = new QMenu(tr("Edit"));
    QMenu* helpMenu = new QMenu(tr("Help"));
    mainMenuBar->addMenu(fileMenu);
    mainMenuBar->addMenu(editMenu);
    mainMenuBar->addMenu(helpMenu);
}

2. Add Menu Action Items

Qt does not have a separate class for menu items; clickable actions are abstracted by the QAction class, which can be reused for both menus and toolbars.

// Add actions to the file menu
QAction* openAct = new QAction(tr("Open"));
QAction* saveAct = new QAction(tr("Save"));
QAction* exitAct = new QAction(tr("Quit"));
fileMenu->addAction(openAct);
fileMenu->addAction(saveAct);
fileMenu->addAction(exitAct);

You can add separator lines between related menu items using QMenu::addSeparator():

fileMenu->addAction(openAct);
fileMenu->addSeparator();
fileMenu->addAction(saveAct);
fileMenu->addSeparator();
fileMenu->addAction(exitAct);

3. Complete Working Example

The following example creates a simple notepad skeleton with save and load functionality:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle(tr("Simple Notepad"));

    // Set up menu bar
    QMenuBar* mainMenuBar = new QMenuBar(this);
    setMenuBar(mainMenuBar);

    // Create file menu
    QMenu* fileMenu = new QMenu(tr("File"));
    mainMenuBar->addMenu(fileMenu);

    // Add action items
    QAction* saveAct = new QAction(tr("Save"));
    QAction* loadAct = new QAction(tr("Load"));
    fileMenu->addAction(saveAct);
    fileMenu->addAction(loadAct);

    // Set up central text edit widget
    textEdit = new QTextEdit(this);
    setCentralWidget(textEdit);
    textEdit->setPlaceholderText(tr("Enter your text here"));

    // Connect actions to handler slots
    connect(saveAct, &QAction::triggered, this, &MainWindow::saveContent);
    connect(loadAct, &QAction::triggered, this, &MainWindow::loadContent);
}

Implement the save and load slots using QFileDialog and standard C++ file operations:

void MainWindow::saveContent()
{
    // Open save file dialog
    QString filePath = QFileDialog::getSaveFileName(
        this, tr("Save File"), QDir::homePath()
    );
    if (filePath.isEmpty()) return;

    // Write content to file
    std::ofstream outFile(filePath.toStdString().c_str());
    if (!outFile.is_open()) {
        qDebug() << "Failed to open file for writing";
        return;
    }
    std::string content = textEdit->toPlainText().toStdString();
    outFile << content;
    outFile.close();
}

void MainWindow::loadContent()
{
    // Open load file dialog
    QString filePath = QFileDialog::getOpenFileName(
        this, tr("Load File"), QDir::homePath()
    );
    if (filePath.isEmpty()) return;

    // Read content from file
    std::ifstream inFile(filePath.toStdString().c_str());
    if (!inFile.is_open()) {
        qDebug() << "Failed to open file for reading";
        return;
    }
    std::string fullContent;
    std::string line;
    while (std::getline(inFile, line)) {
        fullContent += line;
        fullContent += "\n";
    }
    inFile.close();

    // Display loaded content
    textEdit->setPlainText(QString::fromStdString(fullContent));
}

Toolbars

Toolbars are movable areas that provide quick access to frequently used application functions. An application can have zero or multiple toolbars, they can be dragged between different docking areas and even floated as separate windows. Toolbars can hold any Qt widget type, most common icon-based action buttons.

1. Create Toolbars

Add new toolbars to QMainWindow with the addToolBar() method, one call per toolbar:

QToolBar* toolbar1 = new QToolBar(this);
QToolBar* toolbar2 = new QToolBar(this);
this->addToolBar(toolbar1);
this->addToolBar(toolbar2);

2. Set Allowed Docking Positions

You can specify allowed docking positions in two ways: during creation, or after creation via setAllowedAreas(). Available docking areas are:

  • Qt::LeftToolBarArea: Dock to the left edge
  • Qt::RightToolBarArea: Dock to the right edge
  • Qt::TopToolBarArea: Dock to the top edge
  • Qt::BottomToolBarArea: Dock to the bottom edge
  • Qt::AllToolBarAreas: Allow docking to all positions

Specify position during creation:

QToolBar* leftToolbar = new QToolBar(this);
QToolBar* rightToolbar = new QToolBar(this);
this->addToolBar(Qt::LeftToolBarArea, leftToolbar);
this->addToolBar(Qt::RightToolBarArea, rightToolbar);

Set allowed areas after creation:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QToolBar* toolbar1 = new QToolBar(this);
    QToolBar* toolbar2 = new QToolBar(this);
    addToolBar(toolbar1);
    addToolBar(toolbar2);
    // Restrict each toolbar to a single position
    toolbar1->setAllowedAreas(Qt::LeftToolBarArea);
    toolbar2->setAllowedAreas(Qt::RightToolBarArea);
}

3. Set Floating Property

Control whether a toolbar can be floated as a separate window with the setFloatable() method:

void setFloatable (bool floatable)
// true = allow floating, false = disable floating

Example:

toolbar1->setFloatable(true);
toolbar2->setFloatable(false);

4. Complete Toolbar Example

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    resize(800, 600);

    QToolBar* mainToolbar = new QToolBar(this);
    addToolBar(Qt::LeftToolBarArea, mainToolbar);

    // Allow docking only on left/right edges
    mainToolbar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);
    // Disable floating
    mainToolbar->setFloatable(false);
    // Disable moving between dock areas
    mainToolbar->setMovable(false);

    // Add actions to toolbar
    QAction* newAct = new QAction(tr("New File"), this);
    QAction* openAct = new QAction(tr("Open"), this);
    mainToolbar->addAction(newAct);
    mainToolbar->addSeparator();
    mainToolbar->addAction(openAct);

    // Add a standard button widget to toolbar
    QPushButton* saveBtn = new QPushButton(tr("Save"), this);
    mainToolbar->addSeparator();
    mainToolbar->addWidget(saveBtn);
}

Status Bar

The status bar is located at the bottom of the main window, a main window can only have one status bar. It is used to display status information to the user, implemented with the QStatusBar class. Status bar can display three types of information:

  • Temporary messages that auto-expire after a timeout
  • Permanent messages that stay visible
  • Progress indicators for long-running operations

1. Create Status Bar

// Create and attach status bar
QStatusBar* appStatusBar = statusBar();
setStatusBar(appStatusBar);

2. Display Temporary Messages

Show temporary messages with showMessage(), which accepts a timeout in milliseconds:

appStatusBar->showMessage(tr("File saved successfully"), 2000);

3. Display Permanent Messages

Permanent messages are added as child widgets, use addPermanentWidget() to add items to the right side of the status bar:

// Left-aligned permanent label
QLabel* statusLabel = new QLabel(tr("Ready"), this);
appStatusBar->addWidget(statusLabel);

// Right-aligned permanent label
QLabel* versionLabel = new QLabel(tr("v1.0.0"), this);
appStatusBar->addPermanentWidget(versionLabel);

Dock Widgets

Dock widgets (also called floating/riveted widget) are anchorable windows that sit around the central widget of the main window. An application can have multiple dock widgets, implemented with the QDockWidget class.

1. Create Dock Widget

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QDockWidget* projectDock = new QDockWidget(tr("Project Browser"), this);
    addDockWidget(Qt::LeftDockWidgetArea, projectDock);
}

2. Set Allowed Docking Areas

Similar to toolbars, you can restrict where dock widgets can be anchored. Available areas are:

  • Qt::LeftDockWidgetArea
  • Qt::RightDockWidgetArea
  • Qt::TopDockWidgetArea
  • Qt::BottomDockWidgetArea
  • Qt::AllDockWidgetAreas

Example that allows only top/bottom docking:

projectDock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);

Dialogs

Dialogs are top-level windows used for short-term user interactions, for functionality that does not fit in the main window. Qt provides a range of built-in dialogs for common use cases.

1. Dialog Types

Dialogs are divided into three types by behavior: modal, non-modal, and half-modal.

Modal Dialogs

Modal dialogs block interaction with the parent window until they are closed, launched with QDialog::exec(). They are used for mandatory user input like file selection and confirmation prompts.

Example:

connect(newAct, &QAction::triggered, this, [=](){
    QDialog newDlg(this);
    newDlg.resize(200, 100);
    newDlg.exec();
});

Non-Modal (Modeless) Dialogs

Non-modal dialogs exist independently of the parent window, allowing interaction with both the dialog and main window, launched with QDialog::show(). They are usually allocated on the heap to avoid premature destruction, and Qt::WA_DeleteOnClose is set to automatically free memory when the dialog is closed.

Example:

connect(newAct, &QAction::triggered, this, [=](){
    QDialog* findDlg = new QDialog(this);
    findDlg->resize(200, 100);
    findDlg->setAttribute(Qt::WA_DeleteOnClose);
    findDlg->show();
});

Half-Modal Dialogs

Half-modal dialogs combine the memory management of non-modal dialogs with the blocking behavior of modal dialogs, created with QDialog::setModal(true):

connect(newAct, &QAction::triggered, this, [=](){
    QDialog* settingsDlg = new QDialog(this);
    settingsDlg->setAttribute(Qt::WA_DeleteOnClose);
    settingsDlg->setModal(true);
    settingsDlg->resize(200, 100);
    settingsDlg->show();
});

2. Built-in Qt Standard Dialogs

Qt provides a set of reusable built-in dialogs that inherit from QDialog for common use cases:

QMessageBox

Used to show important prompts and get user confirmation. Example:

resize(800, 600);
QPushButton* msgBtn = new QPushButton(tr(Show Confirmation), this);
QMessageBox* msgBox = new QMessageBox(this);

msgBox->setWindowTitle(tr("Unsaved Changes"));
msgBox->setText(tr("Do you want to save changes before closing?"));
msgBox->setIcon(QMessageBox::Question);
msgBox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);

connect(msgBtn, &QPushButton::clicked, msgBox, &QMessageBox::show);

QColorDialog

Lets users select a custom color. Example:

#include <QPushButton>
#include <QColorDialog>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(800, 600);
    QPushButton* colorBtn = new QPushButton(tr("Pick Color"), this);
    QColorDialog* colorDlg = new QColorDialog(this);

    connect(colorBtn, &QPushButton::clicked, [=](){
        QColor selected = colorDlg->getColor(QColor(255, 0, 0));
        qDebug() << "R:" << selected.red() << "G:" << selected.green() << "B:" << selected.blue();
    });
}

QFileDialog

Used for opening and saving files. Common convenience methods:

  • getOpenFileName(): Open a single file and return its path
  • getOpenFileNames(): Open multiple files and return their paths
  • getSaveFileName(): Open a save dialog and return the selected output path

Example opening an image file:

#include <QPushButton>
#include <QFileDialog>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(800, 600);
    QPushButton* openBtn = new QPushButton(tr("Open Image"), this);
    QFileDialog* fileDlg = new QFileDialog(this);

    connect(openBtn, &QPushButton::clicked, [=](){
       QString filePath = fileDlg->getOpenFileName(
           this, tr("Open Image"), 
           QStandardPaths::writableLocation(QStandardPaths::PicturesLocation), 
           tr("Image Files (*.jpg *.png)")
       );
       qDebug() << "Selected file:" << filePath;
    });
}

QFontDialog

Pre-built dialog for selecting font properties. Example:

#include <QPushButton>
#include <QFontDialog>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(800, 600);
    QPushButton* fontBtn = new QPushButton(tr("Select Font"), this);

    connect(fontBtn, &QPushButton::clicked, [=](){
        bool ok;
        QFont selected = QFontDialog::getFont(&ok, QFont("Arial", 12), this);
        if (!ok) return;

        qDebug() << "Family:" << selected.family();
        qDebug() << "Size:" << selected.pointSize();
        qDebug() << "Bold:" << selected.bold();
        qDebug() << "Italic:" << selected.italic();
    });
}

QInputDialog

Pre-built dialog for getting short user input, supports numeric input and list selection. Example selecting an item from a list:

#include <QPushButton>
#include <QInputDialog>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(800, 600);
    QPushButton* selectBtn = new QPushButton(tr("Select Season"), this);
    QInputDialog* inputDlg = new QInputDialog(this);

    connect(selectBtn, &QPushButton::clicked, [=](){
        QStringList seasons;
        seasons << tr("Spring") << tr("Summer") << tr("Fall") << tr("Winter");

        QString selected = inputDlg->getItem(
            this, tr("Select Season"), tr("Season:"), seasons
        );
        qDebug() << "Selected:" << selected;
    });
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.