Qt C++系统资源监控工具开发实战:跨平台实现CPU内存磁盘网络监控
大家好我是专注于分享Qt/C实战经验的博主。在课程设计或毕业设计项目中一个既能展示C功底又能体现Qt跨平台GUI开发能力的“系统资源监控工具”是很多同学的首选。这类项目看似简单但涉及系统API调用、多线程、实时数据可视化等多个核心知识点网上资料往往零散不成体系。本文将手把手带你从零构建一个功能完整的Qt C系统资源监控工具涵盖CPU、内存、磁盘、网络等核心指标的实时监控与图表展示。文章不仅提供可直接运行的完整代码还会深入讲解每个模块的设计原理、跨平台兼容性处理以及开发中常见的“坑点”。无论你是正在寻找课设/大作业选题的学生还是希望提升Qt实战能力的开发者都能从本文中获得一套可直接复用的解决方案。1. 项目概述与核心技术栈1.1 什么是系统资源监控工具系统资源监控工具顾名思义是一个用于实时采集、显示和分析计算机硬件资源如CPU、内存、磁盘、网络使用情况的软件。它通过周期性地查询操作系统提供的接口API来获取各项指标数据并以图形化界面GUI的形式直观地展示给用户例如动态变化的曲线图、仪表盘或数字面板。在课程设计或大作业中开发这样一个工具具有多重价值综合性强融合了C核心语法、面向对象设计、Qt GUI编程、多线程、系统编程等多个知识点。实践价值高模拟了真实世界中运维监控、性能分析工具的开发场景。可扩展性好基础框架搭建完成后可以轻松添加监控进程、GPU温度、电池状态等更多功能。跨平台潜力基于Qt框架项目可以相对容易地移植到Windows、Linux、macOS等不同操作系统。1.2 核心功能设计我们计划实现一个具备以下核心功能的监控工具CPU监控实时显示总体CPU使用率以及每个逻辑核心的使用率。内存监控实时显示物理内存和虚拟内存交换空间的使用量、总量及使用百分比。磁盘监控显示各磁盘分区的总容量、已用空间、可用空间及使用率。网络监控实时显示指定网络接口的上传和下载速度。数据可视化使用Qt Charts模块绘制CPU和内存使用率的历史趋势曲线图。界面布局采用清晰的仪表盘和图表布局数据更新流畅。1.3 技术栈选型与原理C作为核心开发语言负责底层逻辑和系统API调用。其高性能特性适合处理高频的数据采集任务。Qt 5/6作为GUI框架提供跨平台的窗口、控件和图表组件。我们将主要使用QWidgets和QtCharts模块。系统APIWindows使用Windows.h中的PDH性能数据助手系列函数或GetSystemTimes等API。Linux/macOS通过读取/proc文件系统Linux或调用sysctl、vm_stat等接口来获取信息。多线程 (QThread)将耗时的数据采集任务放在独立线程中避免阻塞主UI线程保证界面流畅。定时器 (QTimer)用于周期性地触发数据采集和界面更新。设计原理工具的核心是一个“生产者-消费者”模型。后台工作线程生产者定时从系统API读取数据放入缓冲区或直接通过信号槽机制发送主UI线程消费者接收数据并更新图表和控件。Qt的信号槽机制是线程间通信的安全桥梁。2. 开发环境搭建2.1 Qt安装与配置本项目基于Qt 5.15 LTS或Qt 6.2版本进行开发这两个版本稳定且社区支持好。下载安装访问Qt官网下载Qt Online Installer。安装时务必勾选以下组件Qt套件如Qt 5.15.2或Qt 6.5.0。编译器Windows下选择MinGW 64-bit或MSVCLinux下通常用GCCmacOS下用Clang。关键模块Qt Charts用于绘图。在安装器的“Select Components”步骤中展开Qt版本找到Qt Charts并勾选。验证安装打开Qt Creator新建一个Qt Widgets Application项目编译并运行。如果能成功弹出窗口则基础环境配置正确。2.2 项目创建与模块配置在Qt Creator中新建项目选择Qt Widgets Application。给项目命名例如SystemMonitor。在项目配置文件.pro文件中需要添加charts模块以支持图表功能。打开SystemMonitor.pro添加一行QT core gui charts对于Qt 6可能还需要添加core5compat如果使用旧式信号槽语法但推荐使用新语法。为了支持C11及以上特性可以在.pro文件中添加CONFIG c112.3 跨平台编译注意事项Windows使用MinGW或MSVC编译均可。如果使用MSVC请确保已安装对应版本的Visual Studio Build Tools。Linux需要安装Qt的开发包和必要的系统库。在Ubuntu/Debian上可以使用sudo apt install build-essential qtbase5-dev qtcharts5-dev。macOS通过Homebrew安装Qt或使用官方安装包。确保Xcode Command Line Tools已安装。3. 系统信息采集模块实现核心这是项目的基石我们需要为不同平台编写特定的代码来获取系统数据。我们将创建一个名为SystemInfo的类来封装这些平台相关的细节。3.1 创建SystemInfo类首先在项目中新建一个C类命名为SystemInfo。systeminfo.h头文件#ifndef SYSTEMINFO_H #define SYSTEMINFO_H #include QObject #include QVector class SystemInfo : public QObject { Q_OBJECT public: explicit SystemInfo(QObject *parent nullptr); ~SystemInfo(); // CPU信息 struct CpuData { double totalUsage; // 总使用率 QVectordouble coreUsage; // 每个核心的使用率 }; // 内存信息 struct MemoryData { quint64 totalPhysical; // 物理内存总量 (Bytes) quint64 usedPhysical; // 已用物理内存 (Bytes) quint64 totalSwap; // 交换分区总量 (Bytes) quint64 usedSwap; // 已用交换分区 (Bytes) }; // 磁盘信息 struct DiskData { QString name; // 磁盘名 (如 C:) quint64 totalBytes; // 总容量 quint64 freeBytes; // 可用容量 quint64 availableBytes; // 可用容量 (同free为兼容性保留) }; // 网络信息 struct NetworkData { QString interfaceName; // 接口名 quint64 bytesReceived; // 接收总字节数 quint64 bytesSent; // 发送总字节数 }; // 获取数据的接口函数 CpuData getCpuData(); MemoryData getMemoryData(); QVectorDiskData getDiskData(); NetworkData getNetworkData(const QString interfaceName); signals: // 可以定义信号当数据更新时发出用于高级异步设计 void cpuDataUpdated(const CpuData data); void memoryDataUpdated(const MemoryData data); private: // 平台相关的私有实现函数 void initCpuMeasurement(); CpuData calculateCpuUsage(); MemoryData fetchMemoryInfo(); QVectorDiskData fetchDiskInfo(); NetworkData fetchNetworkInfo(const QString interfaceName); // 平台相关的成员变量用于存储上一次的CPU时间等 #ifdef Q_OS_WIN // Windows-specific members (e.g., PDH query handles) void* m_cpuQuery nullptr; #elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS) // Linux/macOS-specific members QVectorquint64 m_lastCpuTotal; QVectorquint64 m_lastCpuIdle; #endif }; #endif // SYSTEMINFO_H3.2 Windows平台实现 (systeminfo_win.cpp)我们使用Windows.h和Pdh.h性能数据助手来获取信息这是Windows下标准且相对高效的方法。#include systeminfo.h #include windows.h #include pdh.h #include psapi.h // For memory info #include QDebug #include QStorageInfo #include QNetworkInterface #pragma comment(lib, pdh.lib) SystemInfo::SystemInfo(QObject *parent) : QObject(parent) { initCpuMeasurement(); } SystemInfo::~SystemInfo() { #ifdef Q_OS_WIN if (m_cpuQuery) { PdhCloseQuery((PDH_HQUERY)m_cpuQuery); } #endif } void SystemInfo::initCpuMeasurement() { #ifdef Q_OS_WIN PDH_HQUERY cpuQuery; if (PdhOpenQuery(NULL, 0, cpuQuery) ERROR_SUCCESS) { m_cpuQuery cpuQuery; // 添加所有CPU总使用率的计数器 PdhAddEnglishCounter(cpuQuery, L\\Processor(_Total)\\% Processor Time, 0, NULL); // 也可以为每个核心添加计数器这里省略... PdhCollectQueryData(cpuQuery); // 收集初始数据 } #endif } SystemInfo::CpuData SystemInfo::calculateCpuUsage() { CpuData data; data.totalUsage 0.0; #ifdef Q_OS_WIN if (m_cpuQuery) { PDH_HQUERY hQuery (PDH_HQUERY)m_cpuQuery; PdhCollectQueryData(hQuery); PDH_FMT_COUNTERVALUE counterVal; PDH_STATUS status PdhGetFormattedCounterValue( (PDH_HCOUNTER)hQuery, // 这里需要每个计数器的句柄简化处理 PDH_FMT_DOUBLE, NULL, counterVal ); // 注意上述代码是简化版。实际需要为每个计数器单独获取句柄并查询。 // 为了示例清晰我们使用另一种更直接的方法GetSystemTimes FILETIME idleTime, kernelTime, userTime; if (GetSystemTimes(idleTime, kernelTime, userTime)) { static quint64 prevIdle 0, prevKernel 0, prevUser 0; quint64 idle (quint64(idleTime.dwHighDateTime) 32) | idleTime.dwLowDateTime; quint64 kernel (quint64(kernelTime.dwHighDateTime) 32) | kernelTime.dwLowDateTime; quint64 user (quint64(userTime.dwHighDateTime) 32) | userTime.dwLowDateTime; quint64 total (kernel user); quint64 diffTotal total - (prevKernel prevUser); quint64 diffIdle idle - prevIdle; if (diffTotal 0 prevIdle ! 0) { // 不是第一次计算 data.totalUsage (double)(diffTotal - diffIdle) / diffTotal * 100.0; } prevIdle idle; prevKernel kernel; prevUser user; } } #endif // 简化处理只返回总使用率。核心使用率实现类似但需要枚举处理器。 return data; } SystemInfo::MemoryData SystemInfo::fetchMemoryInfo() { MemoryData data {0, 0, 0, 0}; #ifdef Q_OS_WIN MEMORYSTATUSEX memInfo; memInfo.dwLength sizeof(MEMORYSTATUSEX); if (GlobalMemoryStatusEx(memInfo)) { data.totalPhysical memInfo.ullTotalPhys; data.usedPhysical memInfo.ullTotalPhys - memInfo.ullAvailPhys; data.totalSwap memInfo.ullTotalPageFile; data.usedSwap memInfo.ullTotalPageFile - memInfo.ullAvailPageFile; } #endif return data; } QVectorSystemInfo::DiskData SystemInfo::fetchDiskInfo() { QVectorDiskData disks; QListQStorageInfo volumes QStorageInfo::mountedVolumes(); for (const QStorageInfo volume : volumes) { if (volume.isValid() volume.isReady()) { DiskData disk; disk.name volume.rootPath(); disk.totalBytes volume.bytesTotal(); disk.freeBytes volume.bytesFree(); disk.availableBytes volume.bytesAvailable(); disks.append(disk); } } return disks; } SystemInfo::NetworkData SystemInfo::fetchNetworkInfo(const QString interfaceName) { NetworkData data; data.interfaceName interfaceName; static quint64 lastRecv 0, lastSent 0; quint64 currentRecv 0, currentSent 0; QListQNetworkInterface interfaces QNetworkInterface::allInterfaces(); for (const QNetworkInterface interface : interfaces) { if (interface.name() interfaceName interface.isValid()) { QListQNetworkAddressEntry entries interface.addressEntries(); for (const QNetworkAddressEntry entry : entry) { // 统计所有地址的流量简化处理实际应区分IPv4/IPv6 } // Qt本身不直接提供历史流量统计。需要自己记录上一次的字节数。 // 这里使用一个静态变量模拟。生产环境应用更健壮的方法。 QNetworkInterface::InterfaceFlags flags interface.flags(); if (flags QNetworkInterface::IsUp !(flags QNetworkInterface::IsLoopBack)) { currentRecv interface.bytesReceived(); currentSent interface.bytesTransmitted(); break; } } } data.bytesReceived currentRecv; data.bytesSent currentSent; // 计算速度需要在外部定时器中进行(current - last) / interval return data; } // 公有接口函数内部调用平台相关的fetch函数 SystemInfo::CpuData SystemInfo::getCpuData() { return calculateCpuUsage(); } SystemInfo::MemoryData SystemInfo::getMemoryData() { return fetchMemoryInfo(); } QVectorSystemInfo::DiskData SystemInfo::getDiskData() { return fetchDiskInfo(); } SystemInfo::NetworkData SystemInfo::getNetworkData(const QString interfaceName) { return fetchNetworkInfo(interfaceName); }3.3 Linux平台实现要点 (systeminfo_linux.cpp)Linux下主要通过读取/proc文件系统的虚拟文件来获取信息。// systeminfo_linux.cpp (部分关键函数示例) #include systeminfo.h #include QFile #include QTextStream #include QStorageInfo #include QNetworkInterface #include QDebug #include sys/sysinfo.h // for sysinfo() SystemInfo::SystemInfo(QObject *parent) : QObject(parent) { // 初始化CPU时间记录 initCpuMeasurement(); } void SystemInfo::initCpuMeasurement() { #ifdef Q_OS_LINUX QFile file(/proc/stat); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in(file); QString line in.readLine(); // 读取第一行即总的cpu信息 QStringList list line.split( , QString::SkipEmptyParts); if (list.size() 4 list[0] cpu) { m_lastCpuTotal.clear(); m_lastCpuIdle.clear(); quint64 total 0; for (int i 1; i list.size(); i) { total list[i].toULongLong(); } quint64 idle list[4].toULongLong(); // idle时间 m_lastCpuTotal.append(total); m_lastCpuIdle.append(idle); } file.close(); } #endif } SystemInfo::CpuData SystemInfo::calculateCpuUsage() { CpuData data; data.totalUsage 0.0; #ifdef Q_OS_LINUX QFile file(/proc/stat); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in(file); QString line in.readLine(); QStringList list line.split( , QString::SkipEmptyParts); if (list.size() 4 list[0] cpu) { quint64 total 0; for (int i 1; i list.size(); i) { total list[i].toULongLong(); } quint64 idle list[4].toULongLong(); if (!m_lastCpuTotal.isEmpty() !m_lastCpuIdle.isEmpty()) { quint64 diffTotal total - m_lastCpuTotal[0]; quint64 diffIdle idle - m_lastCpuIdle[0]; if (diffTotal 0) { data.totalUsage (double)(diffTotal - diffIdle) / diffTotal * 100.0; } } // 更新上一次的值 m_lastCpuTotal[0] total; m_lastCpuIdle[0] idle; } file.close(); } #endif return data; } SystemInfo::MemoryData SystemInfo::fetchMemoryInfo() { MemoryData data {0, 0, 0, 0}; #ifdef Q_OS_LINUX struct sysinfo info; if (sysinfo(info) 0) { // 注意单位转换sysinfo 返回的是 mem_unit (通常是 bytes) quint64 totalPhys info.totalram * info.mem_unit; quint64 freePhys info.freeram * info.mem_unit; quint64 totalSwap info.totalswap * info.mem_unit; quint64 freeSwap info.freeswap * info.mem_unit; data.totalPhysical totalPhys; data.usedPhysical totalPhys - freePhys; data.totalSwap totalSwap; data.usedSwap totalSwap - freeSwap; } #endif return data; } // Disk和Network信息获取与Windows版类似使用QStorageInfo和QNetworkInterface它们是跨平台的。macOS的实现思路与Linux类似但读取系统信息的API不同如使用host_statistics、sysctl等。由于篇幅限制这里不展开但原理相通定义平台相关的宏Q_OS_MAC然后实现对应的私有函数。4. 多线程数据采集与通信为了避免数据采集尤其是频繁读取/proc或调用系统API阻塞UI主线程导致界面卡顿我们必须使用工作线程。4.1 创建工作线程类 MonitorWorker新建一个MonitorWorker类继承自QObject它将在单独的线程中运行。monitorworker.h#ifndef MONITORWORKER_H #define MONITORWORKER_H #include QObject #include QTimer #include systeminfo.h class MonitorWorker : public QObject { Q_OBJECT public: explicit MonitorWorker(QObject *parent nullptr); ~MonitorWorker(); public slots: void startMonitoring(int intervalMs 1000); // 开始监控指定间隔 void stopMonitoring(); // 停止监控 signals: // 定义信号用于将采集到的数据发送给主线程 void cpuDataUpdated(const SystemInfo::CpuData data); void memoryDataUpdated(const SystemInfo::MemoryData data); void diskDataUpdated(const QVectorSystemInfo::DiskData data); void networkDataUpdated(const SystemInfo::NetworkData data); private slots: void onTimeout(); // 定时器超时执行数据采集 private: QTimer *m_timer; SystemInfo *m_systemInfo; QString m_monitoredNetworkInterface; // 要监控的网络接口名 }; #endif // MONITORWORKER_Hmonitorworker.cpp#include monitorworker.h #include QDebug MonitorWorker::MonitorWorker(QObject *parent) : QObject(parent) , m_timer(new QTimer(this)) , m_systemInfo(new SystemInfo(this)) { m_monitoredNetworkInterface eth0; // 默认监控eth0实际应可配置 connect(m_timer, QTimer::timeout, this, MonitorWorker::onTimeout); } MonitorWorker::~MonitorWorker() { stopMonitoring(); } void MonitorWorker::startMonitoring(int intervalMs) { if (!m_timer-isActive()) { m_timer-start(intervalMs); qDebug() Monitoring started with interval intervalMs ms; } } void MonitorWorker::stopMonitoring() { if (m_timer-isActive()) { m_timer-stop(); qDebug() Monitoring stopped; } } void MonitorWorker::onTimeout() { // 1. 获取CPU数据并发送信号 SystemInfo::CpuData cpuData m_systemInfo-getCpuData(); emit cpuDataUpdated(cpuData); // 2. 获取内存数据并发送信号 SystemInfo::MemoryData memData m_systemInfo-getMemoryData(); emit memoryDataUpdated(memData); // 3. 获取磁盘数据频率可以低一些这里每次都会获取 static int diskCounter 0; if (diskCounter % 5 0) { // 每5次采集一次磁盘数据 QVectorSystemInfo::DiskData diskData m_systemInfo-getDiskData(); emit diskDataUpdated(diskData); } diskCounter; // 4. 获取网络数据并计算速度 static quint64 lastRecv 0, lastSent 0; SystemInfo::NetworkData netDataRaw m_systemInfo-getNetworkData(m_monitoredNetworkInterface); // 计算瞬时速度 (Bytes/s) SystemInfo::NetworkData netDataWithSpeed netDataRaw; if (lastRecv 0 lastSent 0) { // 假设定时器间隔是1000ms netDataWithSpeed.bytesReceived (netDataRaw.bytesReceived - lastRecv); // 差值即为每秒字节数 netDataWithSpeed.bytesSent (netDataRaw.bytesSent - lastSent); } else { netDataWithSpeed.bytesReceived 0; netDataWithSpeed.bytesSent 0; } lastRecv netDataRaw.bytesReceived; lastSent netDataRaw.bytesSent; emit networkDataUpdated(netDataWithSpeed); }4.2 在主线程中创建并管理Worker线程在MainWindow的构造函数或初始化函数中设置线程。mainwindow.cpp片段#include mainwindow.h #include ui_mainwindow.h #include monitorworker.h #include QThread #include QDebug MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , m_workerThread(new QThread(this)) , m_worker(new MonitorWorker()) { ui-setupUi(this); setupCharts(); // 初始化图表后面会讲 setupConnections(); // 将worker对象移动到新线程 m_worker-moveToThread(m_workerThread); // 连接线程开始/结束信号到worker的槽 connect(m_workerThread, QThread::started, m_worker, MonitorWorker::startMonitoring); connect(m_workerThread, QThread::finished, m_worker, MonitorWorker::deleteLater); // 连接worker的数据信号到主窗口的更新槽 connect(m_worker, MonitorWorker::cpuDataUpdated, this, MainWindow::onCpuDataUpdated); connect(m_worker, MonitorWorker::memoryDataUpdated, this, MainWindow::onMemoryDataUpdated); connect(m_worker, MonitorWorker::diskDataUpdated, this, MainWindow::onDiskDataUpdated); connect(m_worker, MonitorWorker::networkDataUpdated, this, MainWindow::onNetworkDataUpdated); // 启动线程 m_workerThread-start(); } MainWindow::~MainWindow() { // 优雅地停止线程 m_workerThread-quit(); m_workerThread-wait(); // 等待线程结束 delete ui; } void MainWindow::setupConnections() { // 其他UI信号槽连接... } // 数据更新槽函数示例 void MainWindow::onCpuDataUpdated(const SystemInfo::CpuData data) { // 更新UI上的CPU显示和图表 ui-labelCpuUsage-setText(QString::number(data.totalUsage, f, 1) %); updateCpuChart(data.totalUsage); }5. 使用Qt Charts实现数据可视化Qt Charts模块提供了强大的图表组件非常适合展示时间序列数据。5.1 在项目中启用并初始化Charts确保.pro文件中已添加QT charts。在mainwindow.cpp中包含头文件#include QtCharts using namespace QtCharts;5.2 创建CPU使用率历史曲线图在MainWindow类中创建图表视图和序列。mainwindow.h中添加private: Ui::MainWindow *ui; QThread *m_workerThread; MonitorWorker *m_worker; // 图表相关 QChart *m_cpuChart; QLineSeries *m_cpuSeries; QValueAxis *m_cpuAxisX; QValueAxis *m_cpuAxisY; QChartView *m_cpuChartView; QVectordouble m_cpuHistoryData; // 存储最近N个数据点 int m_maxHistoryPoints 60; // 显示60个点即最近60秒 void setupCpuChart(); void updateCpuChart(double usage);mainwindow.cpp中实现void MainWindow::setupCpuChart() { // 创建图表 m_cpuChart new QChart(); m_cpuChart-setTitle(CPU Usage History); m_cpuChart-setTheme(QChart::ChartThemeDark); // 设置主题 // 创建折线序列 m_cpuSeries new QLineSeries(); m_cpuSeries-setName(CPU %); m_cpuChart-addSeries(m_cpuSeries); // 创建坐标轴 m_cpuAxisX new QValueAxis(); m_cpuAxisY new QValueAxis(); m_cpuAxisX-setRange(0, m_maxHistoryPoints - 1); m_cpuAxisX-setLabelFormat(%d); m_cpuAxisX-setTitleText(Time (s ago)); m_cpuAxisY-setRange(0, 100); // CPU使用率百分比 m_cpuAxisY-setTitleText(Usage (%)); m_cpuChart-addAxis(m_cpuAxisX, Qt::AlignBottom); m_cpuChart-addAxis(m_cpuAxisY, Qt::AlignLeft); m_cpuSeries-attachAxis(m_cpuAxisX); m_cpuSeries-attachAxis(m_cpuAxisY); // 创建图表视图并添加到UI布局中 m_cpuChartView new QChartView(m_cpuChart); m_cpuChartView-setRenderHint(QPainter::Antialiasing); ui-verticalLayout_chartArea-addWidget(m_cpuChartView); // 假设UI中有一个verticalLayout_chartArea } void MainWindow::updateCpuChart(double usage) { m_cpuHistoryData.append(usage); // 保持数据点数量不超过最大值 if (m_cpuHistoryData.size() m_maxHistoryPoints) { m_cpuHistoryData.removeFirst(); } // 清空序列并重新添加点 m_cpuSeries-clear(); for (int i 0; i m_cpuHistoryData.size(); i) { // X轴越新的数据点索引越大但显示为“时间前推”所以用 (m_maxHistoryPoints - 1 - (m_cpuHistoryData.size() - 1 - i)) // 简化直接使用i作为X从0到size-1最新点在最右边。 m_cpuSeries-append(i, m_cpuHistoryData.at(i)); } // 更新X轴范围使其看起来是滚动的 if (m_cpuHistoryData.size() m_maxHistoryPoints) { m_cpuAxisX-setRange(0, m_maxHistoryPoints - 1); } else { m_cpuAxisX-setRange(0, m_cpuHistoryData.size() - 1); } }5.3 设计主界面布局使用Qt Designer设计主窗口界面mainwindow.ui。一个典型的布局可以包含顶部工具栏或菜单开始/停止监控选择网络接口等。左侧仪表盘区域使用QLabel、QProgressBar、QLCDNumber等控件实时显示CPU、内存、磁盘、网络的数值。右侧图表区域放置一个QWidget作为容器例如QFrame在代码中将其布局设置为QVBoxLayout然后将QChartView添加进去。6. 完整项目集成与运行6.1 项目文件结构SystemMonitor/ ├── SystemMonitor.pro # 项目文件 ├── main.cpp # 程序入口 ├── mainwindow.h # 主窗口头文件 ├── mainwindow.cpp # 主窗口实现 ├── mainwindow.ui # 主窗口UI设计文件 ├── systeminfo.h # 系统信息采集类头文件 ├── systeminfo_win.cpp # Windows平台实现 ├── systeminfo_linux.cpp # Linux平台实现 ├── systeminfo_mac.cpp # macOS平台实现可选 ├── monitorworker.h # 工作线程类头文件 └── monitorworker.cpp # 工作线程类实现6.2 编译与运行在Qt Creator中打开SystemMonitor.pro。选择正确的构建套件Kit。点击“构建”按钮或CtrlB编译项目。点击“运行”按钮或CtrlR启动程序。如果一切顺利你将看到一个图形化窗口实时显示系统的CPU、内存等信息并且CPU使用率以动态曲线图的形式展示。7. 常见问题与解决方案 (FAQ)在开发过程中你可能会遇到以下典型问题问题现象可能原因解决方案编译错误找不到QtCharts头文件项目.pro文件中未添加QT charts。检查并修改.pro文件确保已添加该模块。运行时报错QChart未定义未包含正确的头文件或未使用命名空间。在源文件中添加#include QtCharts和using namespace QtCharts;。程序启动后界面卡死或无响应数据采集在UI主线程中进行阻塞了事件循环。确保已将数据采集逻辑移至工作线程QThread。CPU使用率图表不更新或更新慢1. 信号槽连接失败。2. 数据采集间隔太短UI更新跟不上。3. 图表数据点过多绘图性能瓶颈。1. 检查connect语句和线程管理。2. 适当增加采集间隔如1000ms。3. 限制历史数据点的数量如只保留最近60个。Linux下读取/proc/stat失败文件权限问题极罕见或路径错误。确保程序有读取权限。使用QFile的errorString()函数查看具体错误。Windows下PdhOpenQuery失败未链接pdh.lib库或PDH服务异常。在代码中添加#pragma comment(lib, pdh.lib)。以管理员身份运行程序测试。网络速度显示为0或不准1. 网络接口名选择错误如不是eth0。2. 速度计算逻辑有误未保存上一次的字节数。3. Qt的bytesReceived在某些平台/接口上可能不更新。1. 遍历QNetworkInterface::allInterfaces()打印出所有接口名选择正确的活跃接口。2. 检查MonitorWorker::onTimeout()中的速度计算逻辑。3. 考虑使用平台特定的API如Linux的/proc/net/dev获取更精确的网络流量。跨平台编译错误平台特定的代码被错误地包含在了其他平台的编译中。使用Qt提供的宏进行条件编译#ifdef Q_OS_WIN、#ifdef Q_OS_LINUX、#ifdef Q_OS_MAC。8. 项目优化与扩展建议完成基础功能后可以从以下方向进行优化和扩展提升项目的深度和实用性性能优化减少UI更新频率并非每次数据采集都需要立刻更新所有UI控件。可以对数值显示控件使用QTimer进行节流更新如每500ms更新一次而图表可以保持较高频率。图表优化QLineSeries在数据点很多时性能会下降。可以使用QLineSeries::replace()函数批量替换数据而不是逐个append()。或者考虑使用QScatterSeries显示最近的点。智能采集当程序窗口最小化或隐藏时暂停或降低数据采集频率。功能扩展进程监控列出系统进程并显示每个进程的CPU、内存占用。在Windows上使用CreateToolhelp32SnapshotAPI在Linux上读取/proc/[pid]/stat。GPU监控通过NVML (NVIDIA) 或 ADL (AMD) 库获取GPU温度、使用率、显存信息。温度监控读取CPU/主板温度传感器数据Linux下通常通过/sys/class/thermalWindows需借助第三方库如OpenHardwareMonitorLib。警报功能当CPU使用率、内存使用率或温度超过设定阈值时弹出系统通知或改变UI颜色。数据持久化与回看将监控数据保存到SQLite数据库或文件中并提供历史数据查询和图表回放功能。配置化允许用户通过配置文件或设置界面调整监控间隔、选择监控的磁盘/网络接口、设置图表样式等。代码结构优化使用工厂模式将SystemInfo中平台相关的创建逻辑抽象为工厂模式使平台判断更清晰。引入数据模型使用QAbstractItemModel等为进程列表等数据提供模型便于在QTableView或QListView中展示。更好的错误处理在所有系统API调用处添加更细致的错误检查和日志记录。部署与打包Windows使用windeployqt工具自动拷贝依赖的Qt DLL然后使用Inno Setup或NSIS制作安装包。Linux提供AppImage打包或编写.deb/.rpm包描述文件。macOS使用macdeployqt工具并打包成.dmg或.pkg。这个“系统资源监控工具”项目麻雀虽小五脏俱全。它串联起了C面向对象、Qt信号槽与多线程、跨平台系统编程、数据可视化等多个关键技能点。通过动手实现它你不仅能完成一个高质量的课程设计或大作业更能深入理解桌面应用开发的核心流程。建议你在实现基础功能后选择一两个扩展方向深入尝试这会让你的项目脱颖而出。

相关新闻

基于Java+Vue+Spring Boot的课程作业管理系统全栈开发实战

基于Java+Vue+Spring Boot的课程作业管理系统全栈开发实战

课程作业管理系统是高校教学管理信息化的重要一环,旨在解决传统纸质作业或简单文件提交方式带来的效率低下、统计困难、反馈延迟等问题。本文将手把手带你从零开始,使用 Java Vue Spring Boot MySQL 这套主流技术栈,构建一个功能完整、前后…

2026/7/21 3:16:21阅读更多 →
STM32矩阵键盘Proteus仿真:HAL库驱动与行扫描法详解

STM32矩阵键盘Proteus仿真:HAL库驱动与行扫描法详解

你是不是也遇到过这样的问题:想用STM32做一个带按键输入的项目,比如计算器、密码锁或者简单的菜单控制,结果发现需要的按键数量远超单片机可用的GPIO引脚?传统的独立按键方案,一个按键就要占用一个引脚,16个…

2026/7/21 3:16:21阅读更多 →
电脑与手机特殊符号高效输入全攻略

电脑与手机特殊符号高效输入全攻略

1. 为什么我们需要特殊符号输入秘籍?每天在手机和电脑上敲打键盘时,总有些符号让人头疼——不是藏在二级菜单里,就是要切换好几次输入法才能找到。比如常用的版权符号©、温度单位℃、数学符号≠≈,甚至最近流行的网络热词配套…

2026/7/21 3:14:21阅读更多 →
TI C2000 DSP eCAN模块配置与中断处理实战指南

TI C2000 DSP eCAN模块配置与中断处理实战指南

1. 项目概述在汽车电子和工业控制领域,控制器局域网(CAN)总线是连接各个电子控制单元(ECU)的“神经系统”。它不像我们常见的UART或I2C那样主从分明,而更像一个去中心化的“圆桌会议”,所有节点…

2026/7/21 14:55:09阅读更多 →
DINOv3:自监督视觉基础模型的革命性突破

DINOv3:自监督视觉基础模型的革命性突破

DINOv3:自监督视觉基础模型的革命性突破 【免费下载链接】dinov3 Reference PyTorch implementation and models for DINOv3 项目地址: https://gitcode.com/GitHub_Trending/di/dinov3 DINOv3是Meta AI推出的新一代视觉基础模型,通过创新的自监督…

2026/7/21 14:55:09阅读更多 →
机器学习流水线新范式:从数据流图到声明式计算契约

机器学习流水线新范式:从数据流图到声明式计算契约

1. 这不是又一个“Pipeline”概念炒作,而是工程范式的悄然迁移 “机器学习流水线”这个词,我从2015年第一次在Scikit-learn文档里看到 Pipeline 类时就熟得不能再熟了。但过去八年里,我亲手搭过上百条pipeline——从Kaggle新手赛里用 Stan…

2026/7/21 14:55:09阅读更多 →
终极指南:如何用SDR++开启你的软件定义无线电之旅

终极指南:如何用SDR++开启你的软件定义无线电之旅

终极指南:如何用SDR开启你的软件定义无线电之旅 【免费下载链接】SDRPlusPlus Cross-Platform SDR Software 项目地址: https://gitcode.com/GitHub_Trending/sd/SDRPlusPlus 你是否曾好奇过那些看不见的无线电波中隐藏着什么秘密?无论是广播电台…

2026/7/21 14:55:09阅读更多 →
Windows系统优化神器:5分钟彻底清理150+预装应用,让你的电脑焕然一新

Windows系统优化神器:5分钟彻底清理150+预装应用,让你的电脑焕然一新

Windows系统优化神器:5分钟彻底清理150预装应用,让你的电脑焕然一新 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other cha…

2026/7/21 14:55:05阅读更多 →
终极AI净化指南:如何一键清理搜索引擎中的AI生成内容

终极AI净化指南:如何一键清理搜索引擎中的AI生成内容

终极AI净化指南:如何一键清理搜索引擎中的AI生成内容 【免费下载链接】uBlockOrigin-HUGE-AI-Blocklist A huge blocklist of manually curated sites that contain AI generated imagery for uBlock Origin & uBlacklist. 项目地址: https://gitcode.com/Git…

2026/7/21 14:53:04阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/21 0:51:49阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/21 0:51:49阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/21 0:51:49阅读更多 →
Windows+macOS 通用 OpenClaw 部署流程,内置依赖一键启动智能桌面助手

Windows+macOS 通用 OpenClaw 部署流程,内置依赖一键启动智能桌面助手

📌教程适配:OpenClaw v2.7.9 | 兼容 Windows10/11、macOS 双系统 📖前言 当下各类本地 AI 工具层出不穷,多数产品仅能完成文字问答交互,很难直接操控电脑执行实际操作。OpenClaw,业内常称小龙虾 AI&#…

2026/7/21 0:01:46阅读更多 →
Codex 接入后 Bug 反增?复盘从个人演示到团队协作的“流程陷阱”

Codex 接入后 Bug 反增?复盘从个人演示到团队协作的“流程陷阱”

聊《一次Codex项目复盘,问题最后出在流程而不是模型》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。摘要先把这篇文章的目标说清楚:看完之后,你应该能判断这件事值不值得做&…

2026/7/21 0:01:46阅读更多 →
手把手搓一个五子棋游戏,零代码也能当“游戏开发者”

手把手搓一个五子棋游戏,零代码也能当“游戏开发者”

大家好,还是我。前几期带大家做了心情日记本和可视化大屏,后台有朋友留言:“能不能教点好玩的?我想做游戏,但一行代码都不会。”行,这期就安排。今天的目标:从零做一个五子棋游戏。 带AI对战、三…

2026/7/21 0:03:46阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/20 22:51:39阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/20 18:51:18阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/20 18:51:18阅读更多 →