Qt C++ Architect Series – Project Practice Episode 127: QTableView + Model + Delegate Technology – Disk Heartbeat Monitoring Analyzer

Congratulations on visiting the 【JueDingGe Programming】 WeChat public account 【Knowledge Repository】, specially customized for everyoneQt C++ Architect Series – Project Practice Episode 126:This issue focuses on a practical and challenging technical topic – QTableView + QAbstractTableModel + Custom Delegate Project Technology: Disk Heartbeat Monitoring Performance Analyzer.Whether you are a beginner who has just mastered the Qt cross-platform framework and Linux C/C++ backend server development, or a developer who wants to delve into the research of C/C++ system architecture, this technology will open a door to deep learning for you.

【Development Environment】: Win10/11 x64, Qt version 6.8 ;

【Daily sharing of quality projects and cutting-edge technical programming content!】

【Follow the UP master and join the QQ group】: 【895876809 Download project source code (for research and study)】.

1: 【Project】📁 Source Structure

Qt C++ Architect Series - Project Practice Episode 127: QTableView + Model + Delegate Technology - Disk Heartbeat Monitoring Analyzer

2: 【Project】🚀 Running Results

Qt C++ Architect Series - Project Practice Episode 127: QTableView + Model + Delegate Technology - Disk Heartbeat Monitoring Analyzer

3: 【Project】🏗️ Project Overview and Features

1: Project Overview

A cross-platform (verified on Windows 10+) disk usage monitoring tool based on Qt Widgets. It uses a high-performance table drawn with QTableView + QAbstractTableModel + custom delegate, supporting system tray icons, threshold alarms, and persistent settings.

2: Features

Disk Volume Information: Displays the device name, mount point, file system, total capacity, used, remaining, and usage rate for each volume.

High Performance: QTableView + QAbstractTableModel + QStyledItemDelegate draws progress bars, avoiding frequent creation of child controls.

Statistics Card: Top overview (number of volumes, total space, used, remaining), updated in real-time with refresh.

Refresh and Status Bar: Supports manual “Refresh Now” refresh, with the bottom status bar displaying the last update time and overall usage rate.

Settings Dialog: Configurable refresh interval, Warning/Critical thresholds, and whether to enable notifications; settings are persisted via QSettings.

Threshold Alarm: Notifies through the system tray when exceeding thresholds; maintains the last alarm level for each volume to avoid repeated notifications.

System Tray: Minimizes to tray, with tray menu supporting Show / Quit.

Borderless rounded window: Custom title bar and drag movement, modern appearance.

4: 【Project】📋 Source Code Analysis

1: main.cpp File

#include "diskmonitor.h"
#include <QApplication>
#include <QIcon>
int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    QCoreApplication::setOrganizationName("DiskTools");
    QCoreApplication::setApplicationName("DiskMonitorPro");
    app.setAttribute(Qt::AA_UseHighDpiPixmaps);
    app.setWindowIcon(QIcon(":/images/logo.ico"));
    DiskMonitor monitor;
    monitor.setWindowTitle("Disk Storage Core Performance Analyzer");
    monitor.setWindowIcon(QIcon(":/images/logo.ico"));
    monitor.show();
    return app.exec();
}

2: diskmodel.h File

#pragma once
#include <QAbstractTableModel>
#include <QList>
#include <QStorageInfo>
#include <QString>
class DiskModel : public QAbstractTableModel {
    Q_OBJECT
public:
    enum Columns {
        Column_Device = 0,
        Column_MountPoint,
        Column_FileSystem,
        Column_Total,
        Column_Used,
        Column_Free,
        Column_Usage,
        Column_Count
    };
    enum Roles {
        RoleUsagePercent = Qt::UserRole + 1
    };
    explicit DiskModel(QObject *parent = nullptr);
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    void updateFromVolumes(const QList<QStorageInfo> &volumes,
                           qint64 &totalBytesOut,
                           qint64 &usedBytesOut,
                           qint64 &freeBytesOut);
private:
    struct DiskRow {
        QString deviceDisplayName;
        QString mountPoint;
        QString fileSystem;
        qint64 totalBytes = 0;
        qint64 usedBytes = 0;
        qint64 freeBytes = 0;
        double usagePercent = 0.0; // 0-100
    };
    QList<DiskRow> rows;
    static QString formatSize(qint64 bytes);
};

3: diskmodel.cpp File

#include "diskmodel.h"
#include <QIcon>
#include <QString>
#include <QVariant>
#include <QModelIndex>
DiskModel::DiskModel(QObject *parent)
    : QAbstractTableModel(parent) {}
int DiskModel::rowCount(const QModelIndex &parent) const {
    if (parent.isValid()) return 0;
    return rows.size();}
int DiskModel::columnCount(const QModelIndex &parent) const {
    if (parent.isValid()) return 0;
    return Column_Count;}
QVariant DiskModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid()) return QVariant();
    const DiskRow &r = rows.at(index.row());
    if (role == Qt::DisplayRole) {
        switch (index.column()) {
        case Column_Device: return r.deviceDisplayName;
        case Column_MountPoint: return r.mountPoint;
        case Column_FileSystem: return r.fileSystem;
        case Column_Total: return formatSize(r.totalBytes);
        case Column_Used: return formatSize(r.usedBytes);
        case Column_Free: return formatSize(r.freeBytes);
        case Column_Usage: return QString::number(r.usagePercent, 'f', 1) + "%";
        default: return QVariant();
        }
    }
    if (role == Qt::TextAlignmentRole) {
        if (index.column() == Column_Device || index.column() == Column_MountPoint || index.column() == Column_FileSystem) {
            return Qt::AlignCenter; // for visual consistency with original
        }
        return Qt::AlignCenter;
    }
    if (role == RoleUsagePercent && index.column() == Column_Usage) {
        return r.usagePercent;
    }
    if (role == Qt::DecorationRole && index.column() == Column_Device) {
        return QIcon(":/disk.png");
    }
    return QVariant();}
QVariant DiskModel::headerData(int section, Qt::Orientation orientation, int role) const {
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
        switch (section) {
        case Column_Device: return "Device";
        case Column_MountPoint: return "Mount Point";
        case Column_FileSystem: return "File System";
        case Column_Total: return "Total";
        case Column_Used: return "Used";
        case Column_Free: return "Free";
        case Column_Usage: return "Usage";
        default: return QVariant();
        }
    }
    return QAbstractTableModel::headerData(section, orientation, role);}
Qt::ItemFlags DiskModel::flags(const QModelIndex &index) const {
    if (!index.isValid()) return Qt::NoItemFlags;
    return Qt::ItemIsEnabled | Qt::ItemIsSelectable;}
void DiskModel::updateFromVolumes(const QList<QStorageInfo> &volumes,
                                  qint64 &totalBytesOut,
                                  qint64 &usedBytesOut,
                                  qint64 &freeBytesOut) {
    QList<DiskRow> newRows;
    totalBytesOut = usedBytesOut = freeBytesOut = 0;
    for (const QStorageInfo &v : volumes) {
        if (!v.isValid() || (v.isRoot() && !v.isReady())) continue;
        const qint64 total = v.bytesTotal();
        if (total <= 0) continue;
        const qint64 freeB = v.bytesFree();
        const qint64 usedB = total - freeB;
        const double usage = total > 0 ? (double)usedB / (double)total * 100.0 : 0.0;
        DiskRow r;
        r.deviceDisplayName = v.displayName().isEmpty() ? QString::fromUtf8(v.device()).split('/').last() : v.displayName();
        r.mountPoint = v.rootPath();
        r.fileSystem = QString::fromUtf8(v.fileSystemType());
        r.totalBytes = total;
        r.usedBytes = usedB;
        r.freeBytes = freeB;
        r.usagePercent = usage;
        newRows.push_back(r);
        totalBytesOut += total;
        usedBytesOut += usedB;
        freeBytesOut += freeB;
    }
    beginResetModel();
    rows.swap(newRows);
    endResetModel();}
QString DiskModel::formatSize(qint64 bytes) {
    constexpr qint64 TB = 1024LL * 1024LL * 1024LL * 1024LL;
    constexpr qint64 GB = 1024LL * 1024LL * 1024LL;
    constexpr qint64 MB = 1024LL * 1024LL;
    if (bytes >= TB) return QString::number((double)bytes / TB, 'f', 1) + " TB";
    if (bytes >= GB) return QString::number((double)bytes / GB, 'f', 1) + " GB";
    if (bytes >= MB) return QString::number((double)bytes / MB, 'f', 1) + " MB";
    return QString::number(bytes) + " bytes";
}

4: diskmonitor.h File

#pragma once
#include <QWidget>
#include <QPoint>
#include <QString>
#include <QPaintEvent>
#include <QMouseEvent>
class QTableView;
class QLabel;
class QTimer;
class QSystemTrayIcon;
class QHBoxLayout;
class QPushButton;
class QPaintEvent;
class QMouseEvent;
class QSortFilterProxyModel;
class DiskModel;
class UsageProgressDelegate;
class SettingsDialog;
class DiskMonitor : public QWidget {
    Q_OBJECT
public:
    explicit DiskMonitor(QWidget *parent = nullptr);
private slots:
    void updateDiskInfo();
private:
    void applyStyle();
    QWidget* createStatBox(const QString &title, const QString &value,
                           const QString &iconName, const QString &color);
    void updateStatBoxText(int index, const QString &value);
    QPushButton* createControlButton(const QString &text);
    void createSystemTray();
protected:
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
private:
    QString formatSize(qint64 bytes);
private:
    QTableView *tableView;
    QLabel *statusLabel;
    QWidget *statBoxes[4];
    QLabel *valueLabels[4];
    int valueLabelCount = 0;
    QTimer *timer;
    QSystemTrayIcon *trayIcon;
    QPoint dragPosition;
    QHBoxLayout *statusBarLayout;
    DiskModel *diskModel;
    QSortFilterProxyModel *proxyModel;
    UsageProgressDelegate *usageDelegate;
    SettingsDialog *settingsDialog;
    int refreshIntervalMs = 3000;
    int warnThresholdPercent = 75;
    int criticalThresholdPercent = 90;
    bool notificationsEnabled = true;
    QHash<QString, int> lastAlertLevelByVolume; // 0=none,1=warn,2=critical
};

5: diskmonitor.cpp File

#include "diskmonitor.h"
#include <QApplication>
#include <QTableView>
#include <QHeaderView>
#include <QLabel>
#include <QTimer>
#include <QStorageInfo>
#include <QVBoxLayout>
#include <QPalette>
#include <QPushButton>
#include <QPixmap>
#include <QIcon>
#include <QGraphicsDropShadowEffect>
#include <QHBoxLayout>
#include <QSystemTrayIcon>
#include <QMenu>
#include <QPainter>
#include <QPainterPath>
#include <QTime>
#include <QSortFilterProxyModel>
#include <QSettings>
#include <QMouseEvent>
#include "diskmodel.h"
#include "usageprogressdelegate.h"
#include "settingsdialog.h"
DiskMonitor::DiskMonitor(QWidget *parent) : QWidget(parent) {
    setWindowTitle("Disk Monitor Pro");
    resize(950, 600);
    setMinimumSize(950, 600);
    setWindowFlags(Qt::FramelessWindowHint);
    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->setContentsMargins(20, 15, 20, 15);
    mainLayout->setSpacing(10);
    setLayout(mainLayout);
    QHBoxLayout *titleLayout = new QHBoxLayout();
    titleLayout->setContentsMargins(0, 0, 0, 10);
    QLabel *iconLabel = new QLabel(this);
    iconLabel->setPixmap(QPixmap(":/icon.png").scaled(32, 32,
                                                      Qt::KeepAspectRatio, Qt::SmoothTransformation));
    titleLayout->addWidget(iconLabel);
    QLabel *titleLabel = new QLabel("Disk Storage Core Performance Analyzer (Disk Storage Analyzer)", this);
    QFont titleFont("Arial", 18, QFont::DemiBold);
    titleLabel->setFont(titleFont);
    titleLabel->setStyleSheet("color: #2C3E50;");
    titleLayout->addWidget(titleLabel);
    titleLayout->addStretch();
    QPushButton *minimizeBtn = createControlButton("−");
    QPushButton *closeBtn = createControlButton("✕");
    connect(minimizeBtn, &QPushButton::clicked, this, &QWidget::showMinimized);
    connect(closeBtn, &QPushButton::clicked, qApp, &QApplication::quit);
    titleLayout->addWidget(minimizeBtn);
    titleLayout->addWidget(closeBtn);
    mainLayout->addLayout(titleLayout);
    QHBoxLayout *statsLayout = new QHBoxLayout();
    statsLayout->setSpacing(15);
    statBoxes[0] = createStatBox("Total Volumes", "0", "disk.png", "#3498DB");
    statBoxes[1] = createStatBox("Total Space", "0 GB", "capacity.png", "#2ECC71");
    statBoxes[2] = createStatBox("Used Space", "0 GB", "used.png", "#E74C3C");
    statBoxes[3] = createStatBox("Free Space", "0 GB", "free.png", "#9B59B6");
    for (int i = 0; i < 4; ++i) {
        statsLayout->addWidget(statBoxes[i]);
    }
    mainLayout->addLayout(statsLayout);
    mainLayout->addSpacing(10);
    QWidget *tableContainer = new QWidget(this);
    tableContainer->setStyleSheet("background-color: #FFFFFF; border-radius: 12px;");
    QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect;
    shadow->setBlurRadius(20);
    shadow->setOffset(0, 5);
    shadow->setColor(QColor(0, 0, 0, 30));
    tableContainer->setGraphicsEffect(shadow);
    QVBoxLayout *containerLayout = new QVBoxLayout(tableContainer);
    containerLayout->setContentsMargins(0, 0, 0, 0);
    tableView = new QTableView(this);
    tableView->horizontalHeader()->setMinimumHeight(40);
    tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    tableView->verticalHeader()->setDefaultSectionSize(30);
    tableView->verticalHeader()->setVisible(false);
    tableView->setAlternatingRowColors(true);
    tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
    tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    tableView->setSelectionMode(QAbstractItemView::NoSelection);
    tableView->setStyleSheet("border: none;");
    diskModel = new DiskModel(this);
    proxyModel = new QSortFilterProxyModel(this);
    proxyModel->setSourceModel(diskModel);
    proxyModel->setDynamicSortFilter(true);
    tableView->setModel(proxyModel);
    usageDelegate = new UsageProgressDelegate(this);
    tableView->setItemDelegateForColumn(DiskModel::Column_Usage, usageDelegate);
    tableView->horizontalHeader()->setSectionResizeMode(DiskModel::Column_Usage, QHeaderView::Fixed);
    tableView->horizontalHeader()->resizeSection(DiskModel::Column_Usage, 180);
    containerLayout->addWidget(tableView);
    mainLayout->addWidget(tableContainer);
    statusBarLayout = new QHBoxLayout();
    statusBarLayout->setContentsMargins(5, 5, 5, 5);
    statusLabel = new QLabel("Initializing...", this);
    statusLabel->setFont(QFont("Arial", 9));
    statusBarLayout->addWidget(statusLabel);
    QPushButton *refreshBtn = new QPushButton("Refresh Now", this);
    refreshBtn->setFont(QFont("Arial", 9));
    refreshBtn->setCursor(Qt::PointingHandCursor);
    refreshBtn->setStyleSheet(
        "QPushButton {"
        "   background-color: #5D6D7E;"
        "   color: white;"
        "   padding: 5px 10px;"
        "   border-radius: 4px;"
        "   border: none;"
        "}"
        "QPushButton:hover {"
        "   background-color: #4A5A6A;"
        "}"
        "QPushButton:pressed {"
        "   background-color: #3C4B59;"
        "}"
        );
    connect(refreshBtn, &QPushButton::clicked, this, &DiskMonitor::updateDiskInfo);
    QPushButton *settingsBtn = new QPushButton("Settings", this);
    settingsBtn->setFont(QFont("Arial", 9));
    settingsBtn->setCursor(Qt::PointingHandCursor);
    settingsBtn->setStyleSheet(
        "QPushButton {"
        "   background-color: #5D6D7E;"
        "   color: white;"
        "   padding: 5px 10px;"
        "   border-radius: 4px;"
        "   border: none;"
        "}"
        "QPushButton:hover {"
        "   background-color: #4A5A6A;"
        "}"
        "QPushButton:pressed {"
        "   background-color: #3C4B59;"
        "}"
    );
    connect(settingsBtn, &QPushButton::clicked, this, [this]{
        if (!settingsDialog) settingsDialog = new SettingsDialog(this);
        settingsDialog->setRefreshIntervalMs(refreshIntervalMs);
        settingsDialog->setWarnThresholdPercent(warnThresholdPercent);
        settingsDialog->setCriticalThresholdPercent(criticalThresholdPercent);
        settingsDialog->setNotificationsEnabled(notificationsEnabled);
        if (settingsDialog->exec() == QDialog::Accepted) {
            refreshIntervalMs = settingsDialog->refreshIntervalMs();
            warnThresholdPercent = settingsDialog->warnThresholdPercent();
            criticalThresholdPercent = settingsDialog->criticalThresholdPercent();
            notificationsEnabled = settingsDialog->notificationsEnabled();
            timer->stop();
            timer->start(refreshIntervalMs);
            QSettings s;
            s.setValue("refreshIntervalMs", refreshIntervalMs);
            s.setValue("warnThresholdPercent", warnThresholdPercent);
            s.setValue("criticalThresholdPercent", criticalThresholdPercent);
            s.setValue("notificationsEnabled", notificationsEnabled);
        }
    });
    statusBarLayout->addStretch();
    statusBarLayout->addWidget(settingsBtn);
    statusBarLayout->addWidget(refreshBtn);
    mainLayout->addLayout(statusBarLayout);
    QSettings s;
    refreshIntervalMs = s.value("refreshIntervalMs", refreshIntervalMs).toInt();
    warnThresholdPercent = s.value("warnThresholdPercent", warnThresholdPercent).toInt();
    criticalThresholdPercent = s.value("criticalThresholdPercent", criticalThresholdPercent).toInt();
    notificationsEnabled = s.value("notificationsEnabled", notificationsEnabled).toBool();
    timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &DiskMonitor::updateDiskInfo);
    timer->start(refreshIntervalMs);
    applyStyle();
    createSystemTray();}
void DiskMonitor::updateDiskInfo() {
    QList<QStorageInfo> volumes = QStorageInfo::mountedVolumes();
    qint64 totalBytes = 0;
    qint64 usedBytes = 0;
    qint64 freeBytes = 0;
    diskModel->updateFromVolumes(volumes, totalBytes, usedBytes, freeBytes);
    // Threshold alerts
    if (notificationsEnabled && trayIcon && trayIcon->isVisible()) {
        for (int r = 0; r < diskModel->rowCount(); ++r) {
            QModelIndex idxUsage = diskModel->index(r, DiskModel::Column_Usage);
            double percent = idxUsage.data(DiskModel::RoleUsagePercent).toDouble();
            QString name = diskModel->index(r, DiskModel::Column_Device).data().toString();
            int level = 0;
            if (percent >= criticalThresholdPercent) level = 2;
            else if (percent >= warnThresholdPercent) level = 1;
            int lastLevel = lastAlertLevelByVolume.value(name, 0);
            if (level > lastLevel) {
                if (level == 2) {
                    trayIcon->showMessage("Disk Critical",
                        QString("%1 usage %2% exceeds %3%")
                        .arg(name)
                        .arg(QString::number(percent, 'f', 1))
                        .arg(criticalThresholdPercent),
                        QIcon(":/icon.png"), 5000);
                } else if (level == 1) {
                    trayIcon->showMessage("Disk Warning",
                        QString("%1 usage %2% exceeds %3%")
                        .arg(name)
                        .arg(QString::number(percent, 'f', 1))
                        .arg(warnThresholdPercent),
                        QIcon(":/icon.png"), 4000);
                }
            }
            lastAlertLevelByVolume.insert(name, level);
        }
    }
    updateStatBoxText(0, QString::number(diskModel->rowCount()));
    updateStatBoxText(1, formatSize(totalBytes));
    updateStatBoxText(2, formatSize(usedBytes));
    updateStatBoxText(3, formatSize(freeBytes));
    statusLabel->setText(QString("Last updated: %1 | %2 volumes detected | System disk usage: %3%")
                         .arg(QTime::currentTime().toString("hh:mm:ss") )
                         .arg(diskModel->rowCount())
                         .arg(totalBytes > 0 ? (usedBytes * 100 / totalBytes) : 0));}
void DiskMonitor::applyStyle() {
    QPalette pal;
    pal.setColor(QPalette::Window, QColor("#ECF0F1"));
    pal.setColor(QPalette::Base, QColor("#FFFFFF"));
    pal.setColor(QPalette::AlternateBase, QColor("#F8F9F9"));
    pal.setColor(QPalette::Text, QColor("#2C3E50"));
    pal.setColor(QPalette::Button, QColor("#5D6D7E"));
    setPalette(pal);
    setAttribute(Qt::WA_TranslucentBackground);
    setStyleSheet(
        "DiskMonitor {"
        "   background-color: #ECF0F1;"
        "   border-radius: 16px;"
        "   border: 1px solid #D5D8DC;"
        "}"
        );
    tableView->setStyleSheet(
        "QTableWidget {"
        "   background-color: white;"
        "   gridline-color: #EAEDED;"
        "   border-radius: 12px;"
        "   border: none;"
        "}"
        "QHeaderView::section {"
        "   background-color: #2C3E50;"
        "   color: white;"
        "   font-weight: bold;"
        "   font-family: 'Arial';"
        "   padding: 12px 8px;"
        "   border: none;"
        "   font-size: 12pt;"
        "}"
        "QTableView::item {"
        "   font-family: 'Arial';"
        "   padding: 5px 8px;"
        "   border: none;"
        "   border-bottom: 1px solid #EAEDED;"
        "}"
        "QTableView::item:alternate {"
        "   background-color: #F8F9F9;"
        "}"
        );
}
QWidget* DiskMonitor::createStatBox(const QString &title, const QString &value,
                           const QString &iconName, const QString &color) {
    QWidget *statBox = new QWidget(this);
    statBox->setMinimumHeight(70);
    statBox->setStyleSheet(
        QString(
            "QWidget {"
            "   background-color: %1;"
            "   border-radius: 8px;"
            "   padding: 10px;"
            "}"
            ).arg(color)
        );
    QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect;
    shadow->setBlurRadius(12);
    shadow->setOffset(0, 4);
    shadow->setColor(QColor(0, 0, 0, 40));
    statBox->setGraphicsEffect(shadow);
    QHBoxLayout *layout = new QHBoxLayout(statBox);
    layout->setContentsMargins(12, 8, 12, 8);
    QLabel *iconLabel = new QLabel();
    iconLabel->setPixmap(QPixmap(QString(":/%1").arg(iconName)).scaled(30, 30,
                                                                       Qt::KeepAspectRatio, Qt::SmoothTransformation));
    iconLabel->setStyleSheet("background: transparent;");
    layout->addWidget(iconLabel);
    QVBoxLayout *textLayout = new QVBoxLayout();
    textLayout->setContentsMargins(10, 0, 0, 0);
    textLayout->setSpacing(2);
    QLabel *titleLabel = new QLabel(title);
    titleLabel->setStyleSheet(
        "color: white;"
        "font-family: 'Arial';"
        "font-size: 10pt;"
        );
    valueLabels[valueLabelCount] = new QLabel(value);
    valueLabels[valueLabelCount]->setStyleSheet(
        "color: white;"
        "font-family: 'Arial';"
        "font-weight: bold;"
        "font-size: 14pt;"
        );
    valueLabels[valueLabelCount]->setTextFormat(Qt::RichText);
    textLayout->addWidget(titleLabel);
    textLayout->addWidget(valueLabels[valueLabelCount]);
    layout->addLayout(textLayout);
    layout->addStretch();
    valueLabelCount++;
    return statBox;
}
void DiskMonitor::updateStatBoxText(int index, const QString &value) {
    if (index < 0 || index >= valueLabelCount) return;
    if (valueLabels[index]) {
        valueLabels[index]->setText(QString("<b>%1</b>").arg(value));
    }}
QPushButton* DiskMonitor::createControlButton(const QString &text) {
    QPushButton *btn = new QPushButton(text, this);
    btn->setFixedSize(30, 30);
    btn->setStyleSheet(
        "QPushButton {"
        "   background-color: #5D6D7E;"
        "   color: white;"
        "   border-radius: 15px;"
        "   font-size: 15px;"
        "   font-weight: bold;"
        "}"
        "QPushButton:hover {"
        "   background-color: #34495E;"
        "}"
        "QPushButton:pressed {"
        "   background-color: #2C3E50;"
        "}"
        );
    return btn;
}
void DiskMonitor::createSystemTray() {
    trayIcon = new QSystemTrayIcon(this);
    trayIcon->setIcon(QIcon(":/icon.png"));
    trayIcon->setToolTip("Disk Monitor Pro");
    QMenu *trayMenu = new QMenu(this);
    trayMenu->setStyleSheet(
        "QMenu {"
        "   background-color: white;"
        "   border: 1px solid #D5D8DC;"
        "   border-radius: 4px;"
        "   padding: 5px;"
        "}"
        "QMenu::item {"
        "   padding: 5px 20px;"
        "   font-family: 'Arial';"
        "}"
        "QMenu::item:selected {"
        "   background-color: #5D6D7E;"
        "   color: white;"
        "   border-radius: 2px;"
        "}"
        );
    QAction *restoreAction = new QAction("Show", this);
    QAction *quitAction = new QAction("Quit", this);
    connect(restoreAction, &QAction::triggered, this, &QWidget::showNormal);
    connect(quitAction, &QAction::triggered, qApp, &QApplication::quit);
    trayMenu->addAction(restoreAction);
    trayMenu->addSeparator();
    trayMenu->addAction(quitAction);
    trayIcon->setContextMenu(trayMenu);
    trayIcon->show();
    connect(trayIcon, &QSystemTrayIcon::activated, this, [=](QSystemTrayIcon::ActivationReason reason){
        if (reason == QSystemTrayIcon::Trigger) {
            if (isHidden()) showNormal();
            else hide();
        }
    });
}
void DiskMonitor::paintEvent(QPaintEvent *event) {
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    QPainterPath path;
    path.addRoundedRect(rect(), 16, 16);
    painter.fillPath(path, palette().window());
    QWidget::paintEvent(event);
}
void DiskMonitor::mousePressEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {
        dragPosition = event->globalPosition().toPoint() - frameGeometry().topLeft();
        event->accept();
    }}
void DiskMonitor::mouseMoveEvent(QMouseEvent *event) {
    if (event->buttons() & Qt::LeftButton) {
        move(event->globalPosition().toPoint() - dragPosition);
        event->accept();
    }}
QString DiskMonitor::formatSize(qint64 bytes) {
    constexpr qint64 TB = 1024LL * 1024LL * 1024LL * 1024LL;
    constexpr qint64 GB = 1024LL * 1024LL * 1024LL;
    constexpr qint64 MB = 1024LL * 1024LL;
    if (bytes >= TB)
        return QString::number((double)bytes / TB, 'f', 1) + " TB";
    else if (bytes >= GB)
        return QString::number((double)bytes / GB, 'f', 1) + " GB";
    else if (bytes >= MB)
        return QString::number((double)bytes / MB, 'f', 1) + " MB";
    else
        return QString::number(bytes) + " bytes";
}

Due to space limitations, please follow the UP master and join the group to download the project source code for research and study.

Leave a Comment