Qt Widgets


  • 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. QWidget Fundamentals

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;           // top-level — gets a frame, title bar
QPushButton *btn = new QPushButton("OK", window);   // child — drawn inside 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);            // bring to front
  • 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.
// Modal (application-modal — blocks every other window)
MyDialog dlg(this);
if (dlg.exec() == QDialog::Accepted) { /* OK pressed */ }

// Modal but only blocks the parent window
dlg.setWindowModality(Qt::WindowModal);
dlg.show();

// Modeless
auto *dlg2 = new MyDialog(this);
dlg2->setAttribute(Qt::WA_DeleteOnClose);   // self-destruct on close
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(),                              // initial dir
    tr("Images (*.png *.jpg);;Text files (*.txt)") // filter
);

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.

2.3 QInputDialog

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) { /* quit */ }

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();          // let UI update
    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").

3. Display Widgets (QFrame family)

  • 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.

4. Buttons

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"));   // & = mnemonic; Alt+O activates
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

  • Single-line text editor.
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;_");   // 0 = required digit; _ = placeholder
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);                // allow free-form entry
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.

7. Sliders: QScrollBar, QSlider, QDial

  • 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