Qt Widgets — Building GUIs

Core widgets, layouts, dialogs, and building desktop/embedded GUIs with Qt Widgets framework.

7 min read
68091 chars

The Qt Widgets Module

Qt Widgets provides a rich set of UI controls (widgets) for building traditional desktop and embedded GUIs. All widgets inherit from QWidget.

QObject
  └── QWidget
        ├── QAbstractButton
        │     ├── QPushButton
        │     ├── QCheckBox
        │     └── QRadioButton
        ├── QLabel
        ├── QLineEdit
        ├── QComboBox
        ├── QSlider
        ├── QSpinBox
        └── QFrame
              └── QAbstractScrollArea
                    └── QScrollArea

Core Widgets

QPushButton

#include <QPushButton>

QPushButton *btn = new QPushButton("Click Me", parent);
btn->setEnabled(true);
btn->setCheckable(true);  // toggle button

connect(btn, &QPushButton::clicked, []() {
    qDebug() << "Button clicked!";
});

QLabel

#include <QLabel>

QLabel *label = new QLabel("Hello Qt!", parent);
label->setAlignment(Qt::AlignCenter);
label->setStyleSheet("font-size: 18px; color: #00aaff;");

// Display an image
label->setPixmap(QPixmap(":/icons/logo.png").scaled(64, 64, Qt::KeepAspectRatio));

QLineEdit

#include <QLineEdit>

QLineEdit *edit = new QLineEdit(parent);
edit->setPlaceholderText("Enter value...");
edit->setMaxLength(20);
edit->setEchoMode(QLineEdit::Password);  // for passwords

connect(edit, &QLineEdit::textChanged, [](const QString &text) {
    qDebug() << "Text:" << text;
});

QComboBox

#include <QComboBox>

QComboBox *combo = new QComboBox(parent);
combo->addItems({"Option A", "Option B", "Option C"});
combo->setCurrentIndex(0);

connect(combo, &QComboBox::currentTextChanged, [](const QString &text) {
    qDebug() << "Selected:" << text;
});

QSlider & QSpinBox

#include <QSlider>
#include <QSpinBox>

QSlider *slider = new QSlider(Qt::Horizontal, parent);
slider->setRange(0, 100);
slider->setValue(50);

QSpinBox *spin = new QSpinBox(parent);
spin->setRange(0, 100);

// Link them
connect(slider, &QSlider::valueChanged, spin, &QSpinBox::setValue);
connect(spin, &QSpinBox::valueChanged, slider, &QSlider::setValue);

Layouts

Layouts manage widget size and position automatically — essential for responsive UIs.

QVBoxLayout / QHBoxLayout

#include <QVBoxLayout>
#include <QHBoxLayout>

QWidget *window = new QWidget;

QVBoxLayout *mainLayout = new QVBoxLayout(window);

QLabel *title = new QLabel("Temperature Monitor");
mainLayout->addWidget(title);

QHBoxLayout *row = new QHBoxLayout;
row->addWidget(new QLabel("Value:"));
row->addWidget(new QSpinBox);
mainLayout->addLayout(row);

QPushButton *btn = new QPushButton("Read");
mainLayout->addWidget(btn);
mainLayout->addStretch();  // push everything to the top

QGridLayout

#include <QGridLayout>

QGridLayout *grid = new QGridLayout(window);

// addWidget(widget, row, col, rowSpan, colSpan)
grid->addWidget(new QLabel("Name:"),    0, 0);
grid->addWidget(new QLineEdit,          0, 1, 1, 2);
grid->addWidget(new QLabel("Port:"),    1, 0);
grid->addWidget(new QSpinBox,           1, 1);
grid->addWidget(new QPushButton("OK"),  2, 2);

QMainWindow

QMainWindow provides a standard application layout with menu bar, toolbars, status bar, and a central widget:

// mainwindow.h
#include <QMainWindow>

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr);

private slots:
    void onFileOpen();
    void onAbout();

private:
    void setupMenuBar();
    void setupStatusBar();
};
// mainwindow.cpp
#include "mainwindow.h"
#include <QMenuBar>
#include <QStatusBar>
#include <QLabel>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
    setWindowTitle("EmbeddexAI Monitor");
    resize(800, 600);

    // Central widget
    QLabel *central = new QLabel("Central Area", this);
    central->setAlignment(Qt::AlignCenter);
    setCentralWidget(central);

    setupMenuBar();
    statusBar()->showMessage("Ready");
}

void MainWindow::setupMenuBar() {
    QMenu *fileMenu = menuBar()->addMenu("&File");
    fileMenu->addAction("&Open", this, &MainWindow::onFileOpen);
    fileMenu->addSeparator();
    fileMenu->addAction("&Quit", qApp, &QCoreApplication::quit);

    QMenu *helpMenu = menuBar()->addMenu("&Help");
    helpMenu->addAction("&About", this, &MainWindow::onAbout);
}

void MainWindow::onFileOpen() {
    statusBar()->showMessage("Opening file...");
}

void MainWindow::onAbout() {
    QMessageBox::about(this, "About", "EmbeddexAI Qt Tutorial\nVersion 1.0");
}

Dialogs

#include <QDialog>
#include <QFileDialog>
#include <QMessageBox>
#include <QInputDialog>

// File open dialog
QString file = QFileDialog::getOpenFileName(
    this, "Open File", "/home", "Config Files (*.cfg *.ini)");

// Message box
QMessageBox::warning(this, "Warning", "Device not connected!");
int ret = QMessageBox::question(this, "Confirm", "Flash firmware?",
                                QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) { /* flash */ }

// Input dialog
bool ok;
QString text = QInputDialog::getText(this, "IP Address", "Enter IP:",
                                     QLineEdit::Normal, "192.168.1.1", &ok);

Stylesheet (QSS)

Style Qt widgets with CSS-like syntax:

qApp->setStyleSheet(R"(
    QWidget {
        background-color: #1e1e2e;
        color: #cdd6f4;
        font-family: 'Fira Code', monospace;
        font-size: 13px;
    }
    QPushButton {
        background-color: #313244;
        border: 1px solid #45475a;
        border-radius: 6px;
        padding: 6px 16px;
    }
    QPushButton:hover {
        background-color: #585b70;
    }
    QPushButton:pressed {
        background-color: #89b4fa;
        color: #1e1e2e;
    }
)");

Summary

WidgetUse Case
QPushButtonTrigger actions
QLabelDisplay text or images
QLineEditSingle-line text input
QComboBoxDrop-down selection
QSlider / QSpinBoxNumeric input
QVBoxLayout / QHBoxLayoutStack widgets vertically / horizontally
QGridLayoutGrid-based positioning
QMainWindowFull app with menus and status bar

Advanced Widgets

QTableWidget — Spreadsheet-Style Data

#include <QTableWidget>

QTableWidget *table = new QTableWidget(4, 3, this);
table->setHorizontalHeaderLabels({"Sensor", "Value", "Unit"});

// Populate
QStringList sensors = {"Temp", "Humidity", "Pressure", "Voltage"};
QStringList values  = {"36.5",  "60.2",     "1013.25",  "3.30"};
QStringList units   = {"°C",    "%RH",      "hPa",      "V"};

for (int row = 0; row < 4; ++row) {
    table->setItem(row, 0, new QTableWidgetItem(sensors[row]));
    table->setItem(row, 1, new QTableWidgetItem(values[row]));
    table->setItem(row, 2, new QTableWidgetItem(units[row]));
}

table->resizeColumnsToContents();

QTabWidget — Tabbed Interface

#include <QTabWidget>

QTabWidget *tabs = new QTabWidget(this);
tabs->addTab(new ConfigPanel,    QIcon(":/icons/settings.svg"), "Settings");
tabs->addTab(new MonitorPanel,   QIcon(":/icons/monitor.svg"),  "Monitor");
tabs->addTab(new LogPanel,       QIcon(":/icons/log.svg"),      "Log");

connect(tabs, &QTabWidget::currentChanged, [](int idx) {
    qDebug() << "Switched to tab" << idx;
});

QSplitter — Resizable Panes

#include <QSplitter>

QSplitter *splitter = new QSplitter(Qt::Horizontal, this);
splitter->addWidget(new QListView);
splitter->addWidget(new QTextEdit);
splitter->setSizes({250, 600});  // initial sizes in pixels

QStackedWidget — Page Switching

#include <QStackedWidget>

QStackedWidget *stack = new QStackedWidget(this);
stack->addWidget(new LoginPage);    // index 0
stack->addWidget(new DashboardPage);// index 1
stack->addWidget(new SettingsPage); // index 2

// Switch pages
stack->setCurrentIndex(1);  // show Dashboard

QProgressBar

#include <QProgressBar>

QProgressBar *bar = new QProgressBar(this);
bar->setRange(0, 100);
bar->setValue(0);
bar->setFormat("Flashing: %p%");
bar->setTextVisible(true);

// Indeterminate (busy)
bar->setRange(0, 0);  // spinning bar

Visual: Layout Reference

┌──────────────────────────────────────────┐
│  QMainWindow                             │
│  ┌────────────────────────────────────┐  │
│  │  Menu Bar                          │  │
│  ├────────────────────────────────────┤  │
│  │  Tool Bar                          │  │
│  ├──────────────┬─────────────────────┤  │
│  │              │                     │  │
│  │  QDockWidget │   Central Widget    │  │
│  │  (sidebar)   │   QStackedWidget    │  │
│  │              │                     │  │
│  ├──────────────┴─────────────────────┤  │
│  │  Status Bar                        │  │
│  └────────────────────────────────────┘  │
└──────────────────────────────────────────┘

Lab 1 — Device Config Form

// configform.h
class ConfigForm : public QWidget {
    Q_OBJECT
public:
    ConfigForm(QWidget *parent = nullptr) : QWidget(parent) {
        auto *form = new QFormLayout(this);

        m_portEdit    = new QLineEdit("/dev/ttyUSB0");
        m_baudBox     = new QComboBox;
        m_baudBox->addItems({"9600","19200","38400","115200"});
        m_baudBox->setCurrentText("115200");
        m_parityBox   = new QComboBox;
        m_parityBox->addItems({"None","Even","Odd"});
        m_connectBtn  = new QPushButton("Connect");

        form->addRow("Port:",    m_portEdit);
        form->addRow("Baud:",    m_baudBox);
        form->addRow("Parity:",  m_parityBox);
        form->addRow(m_connectBtn);

        connect(m_connectBtn, &QPushButton::clicked, this, &ConfigForm::onConnect);
    }

signals:
    void connectRequested(const QString &port, int baud);

private slots:
    void onConnect() {
        emit connectRequested(m_portEdit->text(),
                              m_baudBox->currentText().toInt());
    }

private:
    QLineEdit *m_portEdit;
    QComboBox *m_baudBox;
    QComboBox *m_parityBox;
    QPushButton *m_connectBtn;
};

Lab 2 — Sensor Dashboard

class SensorDashboard : public QWidget {
    Q_OBJECT
public:
    SensorDashboard(QWidget *p = nullptr) : QWidget(p) {
        auto *grid = new QGridLayout(this);

        // Add sensor cards (label + progressbar)
        addCard(grid, "Temperature", 0, 0, 100, "°C");
        addCard(grid, "Humidity",    1, 0, 100, "%");
        addCard(grid, "Pressure",    2, 900, 1100, "hPa");
    }

    void updateSensor(const QString &name, int value) {
        if (m_bars.contains(name)) {
            m_bars[name]->setValue(value);
            m_labels[name]->setText(
                QString("%1: %2").arg(name).arg(value));
        }
    }

private:
    void addCard(QGridLayout *g, const QString &name,
                 int row, int min, int max, const QString &unit)
    {
        auto *label = new QLabel(name + ": --");
        auto *bar   = new QProgressBar;
        bar->setRange(min, max);
        bar->setFormat(QString("%p% (%v " + unit + ")"));
        g->addWidget(label, row, 0);
        g->addWidget(bar,   row, 1);
        m_labels[name] = label;
        m_bars[name]   = bar;
    }

    QHash<QString, QLabel*>       m_labels;
    QHash<QString, QProgressBar*> m_bars;
};

Interview Questions

Q1: What is the difference between a widget and a layout?

A widget is a visual element that can be displayed (button, label). A layout manages the position and size of child widgets within a parent widget. Layouts resize widgets automatically when the window is resized.

Q2: Why use QMainWindow instead of QWidget as the main window?

QMainWindow provides a built-in menu bar, toolbars, dock widgets, and a status bar — features needed by most real applications. For simple dialogs or sub-panels, QWidget is sufficient.

Q3: What happens to heap-allocated child widgets when the parent is destroyed?

Qt’s parent-child system ensures the parent deletes all heap-allocated children. You don’t need to delete child widgets manually if you set a parent.

Q4: How do you make a widget expand to fill available space?

Set a QSizePolicy with Expanding or use setMinimumSize/setMaximumSize. In layouts, call layout->setStretchFactor(widget, 1) to give that widget more space.

Q5: What is QSS and where does it apply?

Qt Style Sheets (QSS) — a CSS subset for styling Qt widgets. Apply to the whole app (qApp->setStyleSheet(...)), a window, or individual widgets. Supports selectors, pseudo-states (:hover, :pressed), and box model properties.


Application: Embedded Device Monitor

// monitor.h
class DeviceMonitor : public QMainWindow {
    Q_OBJECT
public:
    DeviceMonitor(QWidget *p = nullptr) : QMainWindow(p) {
        setWindowTitle("Device Monitor v1.0");
        resize(900, 600);

        // Central splitter
        auto *split = new QSplitter(Qt::Horizontal, this);
        setCentralWidget(split);

        // Left: device list
        m_deviceList = new QListWidget;
        m_deviceList->addItem("UART-0");
        m_deviceList->addItem("SPI-1");
        m_deviceList->addItem("I2C-2");
        split->addWidget(m_deviceList);

        // Right: tabs
        auto *tabs = new QTabWidget;
        tabs->addTab(buildStatusTab(), "Status");
        tabs->addTab(buildLogTab(),    "Log");
        split->addWidget(tabs);
        split->setSizes({200, 700});

        // Status bar
        m_statusBar = new QStatusBar;
        setStatusBar(m_statusBar);
        m_statusBar->showMessage("Ready");

        // Timer: simulate readings
        auto *timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, &DeviceMonitor::updateReadings);
        timer->start(1000);
    }

private slots:
    void updateReadings() {
        m_tempBar->setValue(20 + rand() % 40);
        m_voltBar->setValue(28 + rand() % 10);
        m_log->append(QDateTime::currentDateTime().toString("hh:mm:ss")
                      + " Reading updated");
    }

private:
    QWidget *buildStatusTab() {
        auto *w    = new QWidget;
        auto *form = new QFormLayout(w);
        m_tempBar  = new QProgressBar; m_tempBar->setRange(0, 80);
        m_voltBar  = new QProgressBar; m_voltBar->setRange(0, 50);
        form->addRow("Temperature (°C):", m_tempBar);
        form->addRow("Voltage (0.1V):",   m_voltBar);
        return w;
    }

    QWidget *buildLogTab() {
        auto *w   = new QWidget;
        auto *lay = new QVBoxLayout(w);
        m_log     = new QTextEdit;
        m_log->setReadOnly(true);
        m_log->setFont(QFont("Courier", 10));
        lay->addWidget(m_log);
        return w;
    }

    QListWidget  *m_deviceList;
    QProgressBar *m_tempBar;
    QProgressBar *m_voltBar;
    QTextEdit    *m_log;
    QStatusBar   *m_statusBar;
};

References

ResourceLink
Qt Docs: Widget Classeshttps://doc.qt.io/qt-6/qtwidgets-module.html
Qt Docs: Layout Managementhttps://doc.qt.io/qt-6/layout.html
Qt Docs: QMainWindowhttps://doc.qt.io/qt-6/qmainwindow.html
Qt Docs: Style Sheetshttps://doc.qt.io/qt-6/stylesheet.html

Next tutorial → Qt Object Model & QObject