Building Application Main Windows with Qt QMainWindow
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 edgeQt::RightToolBarArea: Dock to the right edgeQt::TopToolBarArea: Dock to the top edgeQt::BottomToolBarArea: Dock to the bottom edgeQt::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::LeftDockWidgetAreaQt::RightDockWidgetAreaQt::TopDockWidgetAreaQt::BottomDockWidgetAreaQt::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 pathgetOpenFileNames(): Open multiple files and return their pathsgetSaveFileName(): 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;
});
}