news 2026/9/4 15:29:06

C++ Qt实战:打造Windows实时系统监控工具

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ Qt实战:打造Windows实时系统监控工具

这次我们来看一个很适合作为“系统编程第一个工程项目”的 Qt 实战项目:Windows 实时系统监控工具。项目本身不复杂,但把 C++ 系统编程和 Qt 桌面开发最常见的内容都串起来了:Windows 系统 API 调用、定时采集、多线程、图表绘制、进程列表、界面刷新。做完这个项目,你对“程序怎么拿到系统信息”“怎么让界面实时刷新不卡顿”会有非常直观的理解。

这个项目的核心亮点可以概括为:

  • 技术栈干净:C++ + Qt + Windows API,不依赖第三方监控库。
  • 功能覆盖全面:CPU、内存、磁盘、网络、进程列表,基本覆盖系统监控的主要维度。
  • 工程结构清晰:适合按“采集层 + 业务层 + UI 层”拆分,方便后续扩展成托盘工具、远程监控上报工具等。

本文会带大家完成以下内容:从环境梳理开始,逐步拆解采集层、界面层和多线程刷新机制,最后给出功能验证方式和常见问题排查清单。如果你刚学完 C++ 基础,想在 Qt 里做第一个真正有“工程感”的项目,这篇可以直接收藏。

1. 核心能力速览

能力项说明
项目类型Windows 桌面监控工具 / C++ Qt 教学项目
技术栈C++17 + Qt 5.15 / Qt 6.x + Win32 API
主要功能CPU 使用率、内存占用、磁盘容量、网络速率、进程列表实时监控
运行平台Windows 10 / Windows 11(x64)
编译环境MSVC 2019/2022 或 MinGW,Qt Charts 模块
启动方式编译后直接运行 exe
外部依赖仅 Qt、Windows SDK,无第三方监控库
接口 API / 批量任务不涉及,可后续扩展为 HTTP 上报接口
适合场景C++ Qt 学习、系统编程入门、本地性能分析、进程排查

这个项目最大的价值不是“功能多”,而是“知识密度高”。你写一个带界面、带图表、带进程管理的桌面程序,至少要接触 10 个以上 Windows API,还要处理多线程数据传递、Qt 信号槽连接、图表数据更新等问题。一个项目把这些全部过一遍,比单独看语法教程有用得多。

2. 项目架构与核心思路

开发之前,先把架构想清楚。系统监控工具最忌讳的做法是在 UI 线程里直接调用 Windows API 做耗时操作,比如遍历进程列表、读取网络接口状态。如果每秒钟在主线程里跑一次全量采集,界面很容易卡成 PPT。

更好的做法是采用经典的三层结构:

层级职责关键类/模块
采集层调用 Windows API 获取原始系统数据CpuMonitor、MemoryMonitor、DiskMonitor、NetworkMonitor、ProcessMonitor
业务层聚合数据、计算时间增量、维护历史缓冲MonitorManager
UI 层展示数字、曲线、表格,接受用户操作MainWindow、MonitorChart、ProcessTable

数据流向是单向的:

采集线程(Worker) -> 发出 MonitorData 结构体 -> 主线程信号槽 -> 更新 UI

需要注意几个设计点:

  • 采集线程不能直接操作 UI 控件,必须通过信号槽把数据发回主线程。
  • CPU 使用率和网络速率需要计算两次采样之间的差值,不能直接读取瞬时值。
  • 历史曲线需要一个固定长度的环形缓冲或队列,避免图表数据无限增长。
  • UI 刷新频率可以比采集频率低一些,例如采集每 1 秒一次,图表刷新每 1 秒两次,避免 CPU 占用过高。

3. 环境准备与项目初始化

3.1 开发环境

这个项目需要准备的环境非常简单:

  • Qt 5.15 或 Qt 6.x,包含 Qt Charts 模块。
  • 编译器选择 MSVC 2019/2022 或 MinGW。
  • CMake 3.16+ 或 Qt Creator 自带的 qmake。

如果你用 Visual Studio Code 配置 C/C++ 环境,需要提前装好 C/C++ 扩展、CMake Tools 扩展,并在系统 PATH 中配置好 MSVC 或 Qt 的编译器路径。更稳妥的做法是直接用 Qt Creator,它对 Qt 项目的 CMake 配置更加友好,省去很多环境变量问题。

Qt 安装时,如果不知道选哪个编译器套件,可以直接选择“MSVC 2019 64-bit”或者“MinGW 11.2.0 64-bit”,并勾选 Qt Charts 模块。不勾选 Charts 的话,后面find_package(Qt6 COMPONENTS Charts)会直接报错。

3.2 项目目录结构

建议按下面的结构组织项目,采集层和 UI 层分开,后续扩展功能会非常方便:

SystemMonitor/ ├── CMakeLists.txt ├── src/ │ ├── main.cpp │ ├── MainWindow.h │ ├── MainWindow.cpp │ ├── monitors/ │ │ ├── CpuMonitor.h │ │ ├── CpuMonitor.cpp │ │ ├── MemoryMonitor.h │ │ ├── MemoryMonitor.cpp │ │ ├── DiskMonitor.h │ │ ├── DiskMonitor.cpp │ │ ├── NetworkMonitor.h │ │ ├── NetworkMonitor.cpp │ │ ├── ProcessMonitor.h │ │ └── ProcessMonitor.cpp │ ├── worker/ │ │ ├── MonitorWorker.h │ │ └── MonitorWorker.cpp │ └── ui/ │ ├── MonitorChart.h │ ├── MonitorChart.cpp │ ├── ProcessTable.h │ └── ProcessTable.cpp

3.3 CMake 配置模板

下面是一个可以直接用的 CMakeLists.txt 模板。注意,Qt6Qt5需要按你实际安装的版本调整。

cmake_minimum_required(VERSION 3.16) project(SystemMonitor LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) find_package(Qt6 REQUIRED COMPONENTS Widgets Charts) # 如果使用 Qt5,改为: # find_package(Qt5 REQUIRED COMPONENTS Widgets Charts) # Windows API 依赖 if(WIN32) target_link_libraries(SystemMonitor PRIVATE psapi iphlpapi) endif() add_executable(SystemMonitor src/main.cpp src/MainWindow.cpp src/MainWindow.h src/monitors/CpuMonitor.cpp src/monitors/CpuMonitor.h src/monitors/MemoryMonitor.cpp src/monitors/MemoryMonitor.h src/monitors/DiskMonitor.cpp src/monitors/DiskMonitor.h src/monitors/NetworkMonitor.cpp src/monitors/NetworkMonitor.h src/monitors/ProcessMonitor.cpp src/monitors/ProcessMonitor.h src/worker/MonitorWorker.cpp src/worker/MonitorWorker.h src/ui/MonitorChart.cpp src/ui/MonitorChart.h src/ui/ProcessTable.cpp src/ui/ProcessTable.h ) target_link_libraries(SystemMonitor PRIVATE Qt6::Widgets Qt6::Charts )

编译时如果提示找不到Qt6Charts,检查一下 Qt 安装是否勾选了 Charts 模块,或者在 Qt Maintenance Tool 中补充安装。

4. 系统信息采集层设计

采集层是整个项目的核心。每个监视器只负责一类数据的获取,便于单独测试。

4.1 CPU 使用率采集

CPU 使用率不能直接读一次系统时间就得出结果。Windows API 提供GetSystemTimes可以获取系统的空闲时间、内核时间和用户时间,通过两次采样的差值计算出使用率。

#include <windows.h> #include <QtGlobal> class CpuMonitor { public: struct CpuTimes { qint64 idle = 0; qint64 kernel = 0; qint64 user = 0; }; bool snapshot(CpuTimes &times) { FILETIME idle, kernel, user; if (!GetSystemTimes(&idle, &kernel, &user)) { return false; } times.idle = toInt64(idle); times.kernel = toInt64(kernel); times.user = toInt64(user); return true; } double usage() { CpuTimes now; if (!snapshot(now)) { return 0.0; } if (m_last.kernel == 0) { m_last = now; return 0.0; } qint64 idleDelta = now.idle - m_last.idle; qint64 totalDelta = (now.kernel - m_last.kernel) + (now.user - m_last.user); m_last = now; if (totalDelta <= 0) { return 0.0; } return (1.0 - static_cast<double>(idleDelta) / totalDelta) * 100.0; } private: CpuTimes m_last; static qint64 toInt64(const FILETIME &ft) { ULARGE_INTEGER value; value.LowPart = ft.dwLowDateTime; value.HighPart = ft.dwHighDateTime; return static_cast<qint64>(value.QuadPart); } };

这里有一个容易踩的坑:第一次调用usage()时,由于没有上次快照,无法计算差值,应该直接返回 0 或等待第二次采样。实际开发中,第一次采样结果会丢弃。

4.2 内存监控

内存信息通过GlobalMemoryStatusEx一次获取,同时能拿到物理内存总量、可用量、虚拟内存总量和系统内存使用率,占用率直接用系统算好的dwMemoryLoad即可:

#include <windows.h> class MemoryMonitor { public: void snapshot() { MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(memInfo); if (GlobalMemoryStatusEx(&memInfo)) { m_totalPhys = memInfo.ullTotalPhys; m_availPhys = memInfo.ullAvailPhys; m_usagePercent = memInfo.dwMemoryLoad; } } double usagePercent() const { return m_usagePercent; } qint64 totalPhys() const { return static_cast<qint64>(m_totalPhys); } qint64 availPhys() const { return static_cast<qint64>(m_availPhys); } private: DWORD m_usagePercent = 0; ULONGLONG m_totalPhys = 0; ULONGLONG m_availPhys = 0; };

内存监控属于“瞬时值”,不需要差值计算,实现最简单。UI 上可以做成百分比进度条,显示已用内存/总内存。

4.3 磁盘监控

磁盘监控可以对每个逻辑分区单独统计。GetDiskFreeSpaceExW可以获取指定路径所在分区的总容量和剩余容量:

#include <windows.h> #include <QString> class DiskMonitor { public: void snapshot(const QString &path) { ULARGE_INTEGER total, freeBytes; // path 形如 "C:/"、"D:/" const wchar_t *dir = reinterpret_cast<const wchar_t *>(path.utf16()); if (GetDiskFreeSpaceExW(dir, nullptr, &total, &freeBytes)) { m_totalGB = total.QuadPart / (1024.0 * 1024.0 * 1024.0); m_freeGB = freeBytes.QuadPart / (1024.0 * 1024.0 * 1024.0); m_usedPercent = 100.0 * (1.0 - static_cast<double>(freeBytes.QuadPart) / total.QuadPart); } } double totalGB() const { return m_totalGB; } double freeGB() const { return m_freeGB; } double usedPercent() const { return m_usedPercent; } private: double m_totalGB = 0.0; double m_freeGB = 0.0; double m_usedPercent = 0.0; };

如果你的程序要监控“磁盘读写速率”,那就不能靠GetDiskFreeSpaceEx了,需要调用 PDH 计数器或者使用GetDiskPerformance这类接口。对于第一个工程项目,做到分区容量监控已经足够。

4.4 网络流量监控

网络实时速率的计算思路和 CPU 类似:先读取网络接口累计收发字节数,两次采样做差值,再除以时间间隔。

Windows 下可以调用GetIfTable获取网卡接口表,读取dwInOctetsdwOutOctets字段,这两个字段表示接口累计接收和发送的字节数。第一次采集同样需要做基线丢弃。

#include <winsock2.h> #include <iphlpapi.h> #pragma comment(lib, "iphlpapi.lib") class NetworkMonitor { public: struct Snapshot { DWORD inOctets = 0; DWORD outOctets = 0; }; bool read(Snapshot &snap) { ULONG size = 0; if (GetIfTable(nullptr, &size, TRUE) != ERROR_INSUFFICIENT_BUFFER) { return false; } QVector<BYTE> buffer(size); PMIB_IFTABLE table = reinterpret_cast<PMIB_IFTABLE>(buffer.data()); if (GetIfTable(table, &size, TRUE) != NO_ERROR) { return false; } DWORD totalIn = 0, totalOut = 0; for (ULONG i = 0; i < table->dwNumEntries; ++i) { MIB_IFROW &row = table->table[i]; // 忽略 loopback 回环接口,只看实际物理网卡 if (row.dwType == IF_TYPE_SOFTWARE_LOOPBACK) { continue; } totalIn += row.dwInOctets; totalOut += row.dwOutOctets; } snap.inOctets = totalIn; snap.outOctets = totalOut; return true; } // 调用方式:保存上一次快照,计算两次差值后除以采样间隔 double speedKbps(Snapshot prev, Snapshot now, double seconds) { if (seconds <= 0) return 0.0; double inKb = (now.inOctets - prev.inOctets) * 8.0 / 1024.0; return inKb / seconds; } private: Snapshot m_last; };

这里要注意,GetIfTable需要包含winsock2.hiphlpapi.h,在 CMake 里已经链接了iphlpapi。如果你的网络环境中有多个虚拟网卡,统计结果会包含所有接口的汇总,实际显示速度可能比单块物理网卡高。

4.5 进程列表监控

进程列表是系统监控工具里比较有交互感的功能。可以用EnumProcesses枚举 PID,再配合OpenProcessQueryFullProcessImageNameW获取进程路径:

#include <windows.h> #include <psapi.h> #pragma comment(lib, "psapi.lib") #include <QString> #include <QVector> struct ProcessInfo { DWORD pid = 0; QString name; QString path; }; bool collectProcesses(QVector<ProcessInfo> &out) { DWORD processIds[4096]; DWORD bytesReturned = 0; if (!EnumProcesses(processIds, sizeof(processIds), &bytesReturned)) { return false; } DWORD count = bytesReturned / sizeof(DWORD); out.clear(); out.reserve(count); for (DWORD i = 0; i < count; ++i) { DWORD pid = processIds[i]; if (pid == 0) { continue; } HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); if (!process) { continue; } wchar_t buffer[MAX_PATH] = {0}; DWORD bufferSize = MAX_PATH; if (QueryFullProcessImageNameW(process, 0, buffer, &bufferSize)) { ProcessInfo info; info.pid = pid; info.path = QString::fromWCharArray(buffer); info.name = info.path.section('\\', -1); out.append(info); } CloseHandle(process); } return true; }

需要注意:某些系统进程在低权限下无法打开,OpenProcess会返回空句柄,这是正常的,跳过即可。管理员权限运行可以读到更多进程信息。

5. 界面展示与图表刷新设计

界面层我建议用QTabWidget组织页面,一个 Tab 放实时曲线,一个 Tab 放进程列表:

  • “系统概览”页:CPU 使用率曲线、内存使用率曲线、各分区磁盘容量进度条、网络上行/下行速率。
  • “进程管理”页:进程名、PID、路径的表格,支持点击结束进程或刷新列表。

图表部分使用 Qt Charts 的QSplineSeriesQLineSeries,搭配QChartView显示实时曲线。下面给出一个简单示例:

#include <QtCharts/QChartView> #include <QtCharts/QLineSeries> #include <QtCharts/QValueAxis> using namespace QtCharts; class MonitorChart : public QChartView { Q_OBJECT public: explicit MonitorChart(QWidget *parent = nullptr) : QChartView(parent) { m_series = new QLineSeries(); m_chart = new QChart(); m_chart->addSeries(m_series); m_axisX = new QValueAxis(); m_axisX->setRange(0, 60); m_axisY = new QValueAxis(); m_axisY->setRange(0, 100); m_chart->addAxis(m_axisX, Qt::AlignBottom); m_chart->addAxis(m_axisY, Qt::AlignLeft); m_series->attachAxis(m_axisX); m_series->attachAxis(m_axisY); m_chart->legend()->hide(); setChart(m_chart); } void appendValue(double value) { m_series->append(m_index++, value); if (m_series->count() > 60) { m_series->remove(0); } m_chart->axisX()->setRange(m_index - 60, m_index); } private: QLineSeries *m_series; QChart *m_chart; QValueAxis *m_axisX; QValueAxis *m_axisY; qreal m_index = 0; };

曲线只保留最近 60 个点,避免内存和绘制压力无限增长。如果你希望曲线更平滑,可以把QLineSeries换成QSplineSeries,但要注意数据点较多时会有一定 CPU 开销。

6. 多线程与刷新机制设计

系统监控工具要“实时”,但不能因为实时刷新导致界面卡顿,所以采集工作要放到单独线程中。

6.1 Worker 类设计

建议使用QThread + 普通 QObject Worker的方式,而不是直接继承 QThread 重写 run()。原因是 Worker 的生命周期更容易管理,停止线程也更安全。

#include <QObject> #include <QVector> #include "monitors/CpuMonitor.h" #include "monitors/MemoryMonitor.h" #include "monitors/DiskMonitor.h" #include "monitors/NetworkMonitor.h" #include "monitors/ProcessMonitor.h" struct MonitorData { double cpuUsage = 0.0; double memoryUsage = 0.0; double memoryUsedGB = 0.0; double memoryTotalGB = 0.0; double diskUsedPercent = 0.0; double diskFreeGB = 0.0; double netDownloadKbps = 0.0; double netUploadKbps = 0.0; QVector<ProcessInfo> processes; }; class MonitorWorker : public QObject { Q_OBJECT public: explicit MonitorWorker(QObject *parent = nullptr); public slots: void start(); void stop(); signals: void dataReady(const MonitorData &data); private: void collectAndEmit(); CpuMonitor m_cpu; MemoryMonitor m_memory; DiskMonitor m_disk; NetworkMonitor m_network; bool m_running = false; };

collectAndEmit中,把所有采集器的数据聚合到MonitorData结构体,通过emit dataReady(data)发回主线程:

void MonitorWorker::collectAndEmit() { MonitorData data; data.cpuUsage = m_cpu.usage(); m_memory.snapshot(); data.memoryUsage = m_memory.usagePercent(); data.memoryUsedGB = (m_memory.totalPhys() - m_memory.availPhys()) / (1024.0 * 1024.0 * 1024.0); data.memoryTotalGB = m_memory.totalPhys() / (1024.0 * 1024.0 * 1024.0); m_disk.snapshot(QStringLiteral("C:/")); data.diskUsedPercent = m_disk.usedPercent(); data.diskFreeGB = m_disk.freeGB(); // 网络需要根据两次采样间隔计算,这里简化处理 NetworkMonitor::Snapshot now; if (m_network.read(now)) { if (m_lastNet.inOctets != 0) { double seconds = m_timer.elapsed() / 1000.0; data.netDownloadKbps = m_network.speedKbps(m_lastNet, now, seconds); } m_lastNet = now; m_timer.restart(); } collectProcesses(data.processes); emit dataReady(data); }

6.2 主线程接收与定时刷新

主窗口创建一个子线程,把 Worker 移入线程,连接信号槽:

#include "worker/MonitorWorker.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { m_worker = new MonitorWorker(); m_thread = new QThread(this); m_worker->moveToThread(m_thread); connect(m_thread, &QThread::finished, m_worker, &QObject::deleteLater); connect(m_worker, &MonitorWorker::dataReady, this, &MainWindow::updateUI); m_thread->start(); QMetaObject::invokeMethod(m_worker, "start", Qt::QueuedConnection); } void MainWindow::updateUI(const MonitorData &data) { m_cpuChart->appendValue(data.cpuUsage); m_memoryChart->appendValue(data.memoryUsage); m_memoryLabel->setText(QStringLiteral("内存占用:%1% (%2 GB / %3 GB)") .arg(data.memoryUsage, 0, 'f', 1) .arg(data.memoryUsedGB, 0, 'f', 1) .arg(data.memoryTotalGB, 0, 'f', 1)); // 刷新进程表格 refreshProcessTable(data.processes); }

Worker 内部要有停止机制。最简单的做法是增加一个m_running标志,在start()里启动一个QTimer定时执行采集,间隔设为 1000 ms:

void MonitorWorker::start() { if (m_running) return; m_running = true; m_timer.start(1000); } void MonitorWorker::stop() { m_running = false; m_timer.stop(); }

注意,QTimer在 Worker 内部创建后,必须依赖事件循环运行。也就是说,moveToThread之后线程必须exec()进入事件循环,定时器才能触发。如果定时器不工作,先检查线程是否通过start()正常启动了事件循环。

7. 功能测试与效果验证

项目写完后,不要直接开始扩展功能,先跑一轮基础验证。下面是适合这个项目的验证清单。

测试项操作方式预期结果判断成功标准
CPU 监控打开一个多线程耗时任务,比如压缩大文件CPU 曲线上升,空闲时下降曲线变化与任务状态基本一致
内存监控连续打开大型软件或浏览器多个标签页已用内存增加、可用内存减少内存占用数据与任务管理器接近
磁盘监控向监控分区拷贝大文件已用容量上升、剩余容量下降数据刷新及时,不出现明显滞后
网络监控下载大文件或运行网速测试工具下载速率曲线上升数值与资源监视器接近,且上行/下行方向正确
进程列表启动一个测试程序,查看进程列表测试程序在列表中可见能正确显示进程名和 PID
界面流畅度连续运行 5 分钟窗口拖拽、点击无卡顿UI 帧率稳定,CPU 占用率不异常升高
长时间稳定性连续运行 12 小时以上无崩溃、无内存持续增长任务管理器中程序内存占用保持稳定

这里的判断标准是“与任务管理器接近”,而不是完全一致。不同工具刷新频率、采样区间和统计口径不同,数值有 5% 以内的偏差属于正常现象。

如果发现数据一直为 0,先检查采集代码是否真的被调用。可以在 Worker 的collectAndEmit()里临时加一条qDebug()输出,如果日志没有任何打印,说明信号槽连接或者 Worker 线程启动有问题。

8. 常见问题与排查方法

问题现象可能原因排查方式解决方案
编译报错找不到 QtChartsQt 安装时未勾选 Charts 模块检查 Qt 安装目录下是否有Qt6Charts用 Qt Maintenance Tool 补充安装 Charts
中文显示乱码源文件编码或 UI 编码不一致检查源文件是否为 UTF-8源文件统一使用 UTF-8,并在 CMake 中设置编码选项
CPU 占用率过高采集频率过高或 UI 刷新过于频繁观察任务管理器 CPU 占用降低采集频率,曲线只保留最近 60 个点
曲线不更新Worker 的 QTimer 未触发检查线程事件循环是否执行确认moveToThread后线程已exec()
某些进程看不到权限不足对比任务管理器进程数使用管理员权限启动程序
界面卡顿UI 线程中执行了耗时操作检查槽函数里是否有阻塞操作将采集全部移到 Worker 线程
网络速率显示为 0第一次采样被丢弃或表结构读取失败检查返回码确保第一次采样作为基线,第二次采样后开始计算
释放时崩溃线程未正确退出,Worker 被提前销毁查看退出日志在主窗口关闭时先quit()再等待线程结束

一个容易忽视的点是:在关闭窗口时,一定要先停止 Worker 和采集定时器,再退出线程事件循环,最后销毁对象。如果不按顺序处理,可能出现“线程还在执行采集,对象已经被销毁”的崩溃问题。

9. 最佳实践、优化方向与总结

做到这里,一个能用的实时系统监控工具已经成型了。接下来如果想进一步提升,这里提供几个工程化方向:

  • 给每个监视器增加开关,让用户自行决定要显示哪些指标。
  • 把刷新间隔从固定值改成可配置,比如提供 0.5 秒、1 秒、2 秒三档。
  • 进程列表支持按 CPU、内存排序,甚至可以显示单个进程的 CPU 占用。
  • 增加记录日志功能,把采样数据写入本地文件,方便赛后分析。
  • 打包发布时使用windeployqt,把 Qt 运行库和插件一并复制到 exe 目录。

需要注意的是,系统监控工具本身涉及系统资源和进程操作,开发和使用时应限定在自有设备或明确授权的设备范围内。不要把它改造成监控他人设备、绕过系统权限的工具。涉及进程操作时,结束进程前尽量二次确认,避免误杀重要系统进程。

做这类项目的核心经验是:第一次先把“采集 + 简单显示”跑通,再去加图表、加进程管理、加多线程优化。不要在一开始就追求完美的 UI 效果和复杂的动画。项目能跑起来之后,你自然知道哪里需要优化,哪里需要加什么功能。

如果要把这个项目作为简历项目或系统编程练习,建议在 README 里写清楚每个模块用到的 Windows API、QThread 的连接方式、以及监控数据的计算公式。别人看到的不只是一张 UI 截图,而是一套完整的工程思路。

最后给一个很实在的建议:先动手把CpuMonitorMemoryMonitor跑通,再把它们挂到界面显示。这两个模块最简单,也能最快看到效果。跑通之后再考虑网络和进程列表,难度会平滑很多。这个项目做完,你对 C++ Qt 开发、Windows 系统编程的认识会明显上一个台阶。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 16:31:15

MediaPipe 完整指南:5 分钟跑通人脸检测与手部追踪

MediaPipe 完整指南&#xff1a;5 分钟跑通人脸检测与手部追踪 【免费下载链接】mediapipe Cross-platform, customizable ML solutions for live and streaming media. 项目地址: https://gitcode.com/GitHub_Trending/med/mediapipe 你想在应用里加人脸识别或手势控制…

作者头像 李华
网站建设 2026/9/3 9:34:08

PPG脉搏波四层去噪流水线:从PhysioNet数据集到嵌入式部署

简介&#xff1a;本资源是一套面向生物医学信号处理初学者与科研人员的脉搏波&#xff08;PPG&#xff09;去噪实践代码&#xff0c;聚焦PPG-BP公开数据集中的原始脉搏波信号预处理任务&#xff0c;解决噪声干扰下有效特征提取难的问题。压缩包共14个文件&#xff08;6KB&#…

作者头像 李华
网站建设 2026/9/4 18:11:17

FPGA DDR4读写测试实战:从MIG配置到上板调试全解析

简介&#xff1a;本资源为面向FPGA开发者的DDR4内存读写基础实验工程&#xff0c;适用于数字电路、高速接口设计及嵌入式系统方向的学习者与工程师&#xff0c;旨在解决DDR4控制器开发门槛高、时序复杂、调试困难等核心痛点。实验基于Xilinx MIG IP核构建完整DDR4控制方案&…

作者头像 李华
网站建设 2026/9/3 13:06:02

筷子计数专用数据集:VOC+YOLO双格式210张真实场景标注

简介&#xff1a;本资源是一个面向计算机视觉初学者与算法工程师的筷子目标检测专用数据集&#xff0c;适用于YOLO、Faster R-CNN等主流检测模型的训练与验证。数据集包含210张真实场景下的筷子图像&#xff0c;全部采用labelImg工具人工标注&#xff0c;共14872个精确矩形框&a…

作者头像 李华
网站建设 2026/9/4 16:15:13

微信聊天记录怎么导出成HTML、Word、CSV?WeChatMsg 新手完整指南

微信聊天记录怎么导出成HTML、Word、CSV&#xff1f;WeChatMsg 新手完整指南 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trendin…

作者头像 李华
网站建设 2026/9/4 16:53:27

HVE Core代码审查Agent实战:PR前6视角审查让缺陷无处遁形

HVE Core代码审查Agent实战&#xff1a;PR前6视角审查让缺陷无处遁形 【免费下载链接】hve-core A refined collection of Hypervelocity Engineering components (instructions, prompts, agents, and skills) to start your project off right, or upgrade your existing pro…

作者头像 李华