- Description: The Qt Widgets module —
QWidget lifecycle and geometry, dialogs (modal/modeless, file/input/message/progress/error/wizard), display widgets (label, LCD, stacked, toolbox), input widgets (button family, line edit, combo, spin/date), sliders, and rich-text editors
- My Notion Note ID: K2A-B3-2
- Created: 2018-03-04
- Updated: 2026-05-18
- License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io
Table of Contents
1.1 Parent, Window, Ownership
QWidget is the base class for every visible UI element. A widget with no parent is a top-level window; a widget with a parent is a child widget rendered inside the parent's area.
QWidget *window = new QWidget;
QPushButton *btn = new QPushButton("OK", window);
window->show();
- The
parent argument does double duty:
- Sets visual containment (child is clipped + positioned relative to parent).
- Sets ownership — when the parent is deleted, all children are deleted automatically (
QObject ownership tree). Avoid delete child after setParent(parent); it'll be deleted by the parent and you'll double-delete.
- Modern Qt 6 constructor is
QWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()). (Older Qt 4 examples wrote parent = 0, f = 0. Don't copy that style — nullptr and the default-constructed flag enum are correct now.)
1.2 Geometry: Frame vs Content
- Origin = top-left of the screen (or parent's content area for a child). Two coordinate families that catch every beginner:
| Includes window frame (title bar, border)? |
Methods |
| Yes |
frameGeometry(), x(), y(), pos(), move() |
| No |
geometry(), width(), height(), rect(), size() |
move(100, 100) places the top-left of the title bar at (100, 100). resize(800, 600) resizes the client area. They're not symmetric — easy to get bitten by it when restoring window position.
- For multi-monitor / HiDPI work, use
screen() (Qt 5.14+) to query the screen the widget is on; the global desktop is no longer a single coordinate space.
1.3 Window State
window->setWindowState(Qt::WindowMinimized);
window->setWindowState(Qt::WindowMaximized);
window->setWindowState(Qt::WindowFullScreen);
window->setWindowState(Qt::WindowActive);
- States are flags —
Qt::WindowMaximized | Qt::WindowActive is valid. To toggle just one: window->setWindowState(window->windowState() ^ Qt::WindowFullScreen).
- Don't confuse with
setWindowFlags(Qt::FramelessWindowHint) etc., which controls the decoration. State = minimized/maximized/active. Flags = frameless/stays-on-top/tool-window/etc.
1.4 Show, Hide, Close
show() makes the widget visible; first call also creates the platform-specific window.
hide() removes from screen but keeps the widget alive. close() triggers closeEvent() which can be cancelled (return event->ignore()).
- Closing the last visible top-level window does not quit the app by default in Qt 6. Set
QGuiApplication::setQuitOnLastWindowClosed(true) (default) — but tray-icon apps explicitly set this to false to survive hiding the main window.
2. Dialogs
2.1 QDialog — Modal vs Modeless
- Modal blocks interaction with the rest of the app until closed. Modeless coexists.
MyDialog dlg(this);
if (dlg.exec() == QDialog::Accepted) { }
dlg.setWindowModality(Qt::WindowModal);
dlg.show();
auto *dlg2 = new MyDialog(this);
dlg2->setAttribute(Qt::WA_DeleteOnClose);
dlg2->show();
exec() runs a nested event loop — code after it doesn't run until the dialog closes. Convenient, but it means you must not call exec() from inside a signal handler that holds shared state, since other signals will still fire during the nested loop.
- Prefer
show() + signals (accepted, rejected, finished(int)) when you can — avoids the nested event loop and the re-entrancy bugs it brings.
- Three modality levels:
Qt::NonModal — modeless.
Qt::WindowModal — blocks only the parent window (and that window's other dialogs).
Qt::ApplicationModal — blocks every other window in the app.
2.2 QFileDialog
QString file = QFileDialog::getOpenFileName(
this,
tr("Open Image"),
QDir::homePath(),
tr("Images (*.png *.jpg);;Text files (*.txt)")
);
QStringList files = QFileDialog::getOpenFileNames(this, tr("Pick images"), {},
tr("Images (*.png *.jpg)"));
QString dir = QFileDialog::getExistingDirectory(this, tr("Pick a folder"));
QString save = QFileDialog::getSaveFileName(this, tr("Save as"));
- Static functions use the native OS dialog by default on Windows and macOS. Pass
QFileDialog::DontUseNativeDialog if you need the Qt-rendered one (e.g., to test custom widgets, or because the native one is buggy on a specific platform).
- Filter syntax: each filter is
Description (pattern1 pattern2), separated by ;;. Patterns are shell globs.
bool ok;
QString name = QInputDialog::getText(this, tr("Name"), tr("Enter your name:"),
QLineEdit::Normal, "", &ok);
int n = QInputDialog::getInt(this, tr("Count"), tr("How many?"), 1, 0, 100, 1, &ok);
QStringList items = {"Red", "Green", "Blue"};
QString pick = QInputDialog::getItem(this, tr("Color"), tr("Pick:"),
items, 0, false, &ok);
ok distinguishes Cancel (false) from "user pressed OK with empty text" (true).
2.4 QMessageBox
auto reply = QMessageBox::question(this, tr("Quit?"),
tr("Unsaved changes will be lost."),
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) { }
QMessageBox::information(this, tr("Done"), tr("File saved."));
QMessageBox::warning(this, tr("Warning"), tr("Disk almost full."));
QMessageBox::critical(this, tr("Error"), tr("Cannot connect."));
QMessageBox::about(this, tr("About"), tr("MyApp 1.0"));
- For non-standard button layouts, build a
QMessageBox instance, addButton(...) with QMessageBox::ButtonRole, and read clickedButton() after exec().
2.5 QProgressDialog and QErrorMessage
QProgressDialog prog(tr("Copying files..."), tr("Cancel"), 0, totalSteps, this);
prog.setWindowModality(Qt::WindowModal);
for (int i = 0; i < totalSteps; ++i) {
if (prog.wasCanceled()) break;
prog.setValue(i);
QCoreApplication::processEvents();
doStep(i);
}
prog.setValue(totalSteps);
QProgressDialog shows itself automatically after minimumDuration() (default 4s) — quick operations don't flash a dialog.
- The
processEvents call lets the cancel button respond during a synchronous loop. For real work, move it to a QThread or QtConcurrent::run and update the dialog via signals instead — processEvents is a hack that re-enters the event loop.
QErrorMessage::showMessage(QString) queues recurring messages with a "Don't show again" checkbox — useful for non-fatal repeated warnings.
2.6 QWizard
- Multi-page step-through (install wizards, setup flows).
QWizard wiz;
wiz.addPage(new IntroPage);
wiz.addPage(new ConfigPage);
wiz.addPage(new ConfirmPage);
wiz.setWindowTitle(tr("Setup"));
wiz.exec();
- Each
QWizardPage overrides initializePage(), validatePage(), and nextId() for branching. Field registration via registerField("name*", lineEdit) enforces required fields ("*") and lets later pages read them with field("name").
QFrame = base class for widgets that draw a border. Shape (Box, Panel, HLine, etc.) and shadow (Plain, Raised, Sunken) configurable. Useful subclasses below.
| Widget |
What it shows |
QLabel |
Text or pixmap. setText, setPixmap, setMovie for animated GIFs. |
QLCDNumber |
LCD-style digit display — clocks, counters. display(value). |
QStackedWidget |
Holds N child widgets, shows one at a time. setCurrentIndex(i) to switch. Pairs with a QListWidget for tabbed preferences. |
QToolBox |
Stacked panels with collapsible headers. Looks like an accordion. |
QLabel with a QPixmap and setScaledContents(true) is the cheapest "image viewer" — but the pixmap is rescaled on every paint, so for large images, prescale once with QPixmap::scaled(..., Qt::SmoothTransformation) and turn off setScaledContents.
- For showing rich text inline (HTML/Markdown),
QLabel::setText() with Qt::RichText works; for anything bigger than a paragraph use QTextBrowser.
| Class |
Use |
QPushButton |
Standard clicky button. setDefault(true) makes Enter activate it. |
QCheckBox |
Two-state (or three-state with setTristate(true)). |
QRadioButton |
Mutually exclusive within a parent; group them with QButtonGroup if they span layouts. |
QToolButton |
Compact button; supports a popup menu (setMenu) and icon-only display. Used in toolbars. |
QCommandLinkButton |
Big Vista-style "OK to proceed" button — useful in wizards. |
auto *ok = new QPushButton(tr("&OK"));
ok->setDefault(true);
ok->setShortcut(QKeySequence("Ctrl+Return"));
connect(ok, &QPushButton::clicked, this, &MyDialog::accept);
& in the text marks the mnemonic. Show literal & with &&. Same convention for menus, labels (via setBuddy), tab text.
5. QLineEdit
auto *pwd = new QLineEdit;
pwd->setEchoMode(QLineEdit::Password);
pwd->setMaxLength(64);
pwd->setPlaceholderText(tr("Password"));
- Echo modes:
Normal, NoEcho, Password, PasswordEchoOnEdit (clear text while typing, dots after focus loss).
- Input mask — character-class template for fixed-format input:
phone->setInputMask("+1 (000) 000-0000;_");
date->setInputMask("0000-00-00");
- Validators — runtime predicate that rejects invalid input:
edit->setValidator(new QIntValidator(0, 100, edit));
edit->setValidator(new QRegularExpressionValidator(QRegularExpression("[A-Za-z]+"), edit));
- Auto-completion via
QCompleter:
QStringList words = {"apple", "apricot", "banana"};
edit->setCompleter(new QCompleter(words, edit));
6. Selectors: QComboBox, QSpinBox, QDateTimeEdit
auto *combo = new QComboBox;
combo->addItems({"Red", "Green", "Blue"});
combo->setEditable(true);
connect(combo, &QComboBox::currentTextChanged, this, &MyForm::onColorPicked);
auto *spin = new QSpinBox;
spin->setRange(0, 100);
spin->setSingleStep(5);
spin->setSuffix(" %");
auto *dt = new QDateTimeEdit(QDateTime::currentDateTime());
dt->setDisplayFormat("yyyy-MM-dd HH:mm");
dt->setCalendarPopup(true);
QSpinBox is integers. QDoubleSpinBox is floats — pay attention to setDecimals(); the default is 2, which rounds the displayed value if you setValue(0.12345).
QDateTimeEdit::setCalendarPopup(true) adds a calendar dropdown — much more usable for date input than the default spin arrows.
- All three derive from
QAbstractSlider and share its API.
| Property |
Meaning |
minimum / maximum |
Value range. |
value / sliderPosition |
Current value. They differ during a drag if tracking == false. |
singleStep |
Increment from arrow-key / one notch on a scroll wheel. |
pageStep |
Increment from Page Up/Down / clicking the trough. |
tracking |
If true (default), valueChanged() fires continuously while dragging. If false, only on release. |
auto *slider = new QSlider(Qt::Horizontal);
slider->setRange(0, 100);
slider->setSingleStep(1);
slider->setPageStep(10);
slider->setTickPosition(QSlider::TicksBelow);
connect(slider, &QSlider::valueChanged, this, &MyForm::onVolume);
QScrollBar is typically inside a QScrollArea; you rarely instantiate it directly.
QDial is a knob — niche, but right for an audio-pan-style control.
- Set
tracking(false) when each valueChanged triggers an expensive operation (e.g., re-rendering an image); you'll only fire on drag end.
8. Rich Text: QTextEdit, QPlainTextEdit, QTextBrowser
| Widget |
Use when |
QTextEdit |
WYSIWYG rich-text editor — bold/italic, fonts, embedded images, HTML/Markdown via setHtml / setMarkdown. |
QPlainTextEdit |
Plain text only. Significantly faster + lower memory for large logs / source-code editors. |
QTextBrowser |
Read-only rich-text viewer with hyperlink navigation, history, and setSource(QUrl). Handy for in-app help. |
QPlainTextEdit is the right base for a code editor — it has setLineWrapMode, can be paired with a QSyntaxHighlighter, and doesn't degrade on 100k-line files the way QTextEdit does.
QTextEdit::insertHtml injects HTML; if you're displaying untrusted text, wrap it with Qt::convertFromPlainText first or you'll create a stored-XSS vector inside your own app.
9. References