简介基于Qt开发的任务管理器完整项目适合正在学习Qt界面开发、系统级编程的在校生或开发者提供一套可直接编译运行的源码及配套设计报告帮助理解任务管理类工具的实现思路。压缩包共32个文件主要包含cpp/h源文件、ui界面描述、qrc资源以及docx设计报告附带README和LICENSE整体仅1.5MB结构轻量清晰。项目在Qt Creator与Ubuntu 14.04环境编写功能上分为系统信息、进程信息和性能监控三大模块三者置于Tab Widget中通过on_tabWidget_currentChanged槽函数切换刷新状态栏模块显示实时状态菜单栏集成运行新进程与关机命令。源码完整展示了信号槽事件响应、多页界面切换、系统API调用与进程控制等关键技术配合设计报告中关于模块划分和核心流程的说明可快速上手二次开发。目前已有594人学习下载对准备Qt课程设计或相关项目实战的读者有直接参考价值。1. 基于Qt开发实现的任务管理器一份能交作业也能二次开发的Linux系统监控底子任务管理器这类工具很多人觉得只是列一下进程、显示一下CPU真正动手用Qt在Linux下实现一遍才知道光是读/proc、算CPU占用率、处理好UI刷新就能劝退一大批初学者。这套基于Qt开发实现的任务管理器的资源包含完整可编译的Qt Widgets源码和一份设计报告界面把系统信息、进程信息、性能监控装进Tab Widget状态栏放实时信息菜单栏接运行新进程、关机、重启、挂起。适合正在做Qt课程设计、需要Linux进程监控参考代码、或者想往自己工具里集成系统监控模块的开发者。下面按我拆包和复现的顺序把模块逻辑、编译流程、避坑和二次改造要点完整过一遍。2. 模块分区Tab Widget、状态栏和菜单栏怎么分工合作的打开mainwindow.ui就能看到主窗口中央是一个Tab Widget三个页面从上到下依次是系统信息、进程信息、性能。顶部菜单栏放了运行新进程、关机、重启、挂起这类操作入口底部状态栏显示运行时信息。划分很清楚Tab内容负责数据展示菜单栏负责触发动作状态栏负责兜底反馈。这个设计不只是为了好看它决定了代码里不同功能模块的边界在哪。2.1 三个Tab页面与on_tabWidget_currentChanged切换槽这套程序的核心切换逻辑是通过槽函数on_tabWidget_currentChanged来响应Tab页变化。界面上的每个控件都起了名字tabWidget的currentChanged信号直接连接到这个槽。当用户切换到不同页面时槽函数拿到当前页面索引然后分发给对应的刷新函数。代码如下void MainWindow::on_tabWidget_currentChanged(int index) { show_tab_info(index); }这个函数名是Qt Designer自动连接的老约定控件名加下划线加信号名。只要mainwindow.ui里tabWidget对象名没改这个槽就会被自动connect不需要手动写connect语句。它的作用是把页面切换这个动作翻译成刷新哪个页面的逻辑。接下来是分发函数show_tab_info它像一个前台接待根据页码把任务分给不同模块void MainWindow::show_tab_info(int tabIndex) { switch (tabIndex) { case 0: show_system_info(); break; case 1: show_process_info(); break; case 2: show_performance(); break; default: break; } }参数tabIndex是当前Tab页的索引从0开始。第0页是系统信息第1页是进程信息第2页是性能。代码里能看到三个刷新函数各自职责独立互不干扰。这种分发写法比在每个Tab的初始化函数里做一堆判断要清爽后续新增页面只需要加一个case和对应刷新函数。值得注意show_tab_info里没有任何QTimer逻辑它是纯被动刷新——用户切到哪页就刷哪页。这样设计的好处是避免程序一启动就把三个模块全部读一遍浪费资源。但后面我会提到如果想让进程页的内容自动更新还需要再加定时器否则进程列表是静态的。这一点在设计报告的截图里能看出来截图里进程列表数据是有的说明作者至少每次切过去都会重读一次。2.2 系统信息与性能模块数据从哪里来系统信息模块的常规做法是调用uname()系统调用再配合读取/proc/cpuinfo和/proc/version。uname拿到的是操作系统内核层的固定数据比如主机名、内核版本、系统架构。常见实现如下void MainWindow::show_system_info() { struct utsname unameData; if (uname(unameData) 0) { ui-labelHostName-setText(unameData.nodename); ui-labelOsInfo-setText(QString(%1 %2) .arg(unameData.sysname) .arg(unameData.release)); } QFile cpuFile(/proc/cpuinfo); if (cpuFile.open(QIODevice::ReadOnly | QIODevice::Text)) { QString cpuInfo cpuFile.readAll(); // 按行解析 model name 字段 QRegularExpression modelRegex(model name\\s*:\\s*(.*)); QRegularExpressionMatch match modelRegex.match(cpuInfo); if (match.hasMatch()) { ui-labelCpuModel-setText(match.captured(1).trimmed()); } } }这里unameData.nodename是主机名sysname是操作系统名release是内核版本号。读/proc/cpuinfo时用正则抓model name行注意裁剪掉前导空格。这套逻辑在Linux下是稳定的Qt在Windows下则没有/proc所以这套源码天然面向Linux环境移植到Windows需要替换数据源。性能模块的关键是CPU使用率。CPU使用率没法像读温度那样一次性拿结果必须采两次样做差值计算。原理是读取/proc/stat的第一行拿到从系统启动到现在的累计CPU时间隔一段时间再读一次算差值里非空闲时间的比例QString MainWindow::calcCpuUsage() { QFile statFile(/proc/stat); if (!statFile.open(QIODevice::ReadOnly | QIODevice::Text)) return 0.0%; QString firstLine statFile.readLine(); QStringList parts firstLine.split(QRegularExpression(\\s)); parts.removeAll(QString()); // 去掉空字符串 qint64 user parts[1].toLongLong(); qint64 nice parts[2].toLongLong(); qint64 system parts[3].toLongLong(); qint64 idle parts[4].toLongLong(); qint64 total user nice system idle; static qint64 prevTotal 0; static qint64 prevIdle 0; if (prevTotal 0) { prevTotal total; prevIdle idle; return 0.0%; // 第一次采样不计算 } qint64 diffTotal total - prevTotal; qint64 diffIdle idle - prevIdle; prevTotal total; prevIdle idle; if (diffTotal 0) return 0.0%; double usage 1.0 - static_castdouble(diffIdle) / diffTotal; return QString::number(usage * 100.0, f, 1) %; }这里特意用static变量记住上一次的total和idle第一次调用直接返回0.0%等下一次调用才有真实值。注意/proc/stat的字段从第1个索引开始第0个是cpu字符串这是我复现时最容易看错的地方。多核CPU下/proc/stat会有多行每一行代表一个核心如果只取第一行得到的是全核平均占用率这对全局展示是合理的。如果程序里用到了QTimer定时刷新CPU曲线就靠这个函数一次次叠加采样算出来。2.3 状态栏与菜单栏运行新进程、关机重启挂起的实现套路状态栏在mainwindow.cpp的构造函数里初始化。常见做法是new两个QLabeladdWidget到statusBar一个显示当前时间一个显示当前状态文字。时间可以用QTimer每分钟更新一次。菜单栏则通过QAction连接触发函数。运行新进程模块的交互是弹一个输入框让用户输入命令再交给QProcess启动。实现如下void MainWindow::on_actionRunNewProcess_triggered() { QString cmd QInputDialog::getText(this, QStringLiteral(运行新进程), QStringLiteral(输入命令或程序路径)); if (cmd.isEmpty()) return; bool ok QProcess::startDetached(cmd); if (!ok) { QMessageBox::warning(this, QStringLiteral(提示), QStringLiteral(启动失败%1).arg(cmd)); } }startDetached会启动一个独立进程并立刻返回不会阻塞界面。这里有个隐患用户如果输入了带空格和参数的完整命令行比如/home/user/my app --verbosestartDetached会把它当成一个整体路径去解析经常启动失败。更稳妥的写法是拆分成程序路径和参数列表但QInputDialog拿到的是一整个字符串所以原项目直接透传给startDetached。这个具体坑在第4章单独讲。关机、重启、挂起这三个动作本质是调用系统命令。常见写法是void MainWindow::on_actionShutdown_triggered() { QProcess::startDetached(/sbin/shutdown, QStringList() -h now); }菜单栏里对应图标文件分别是shutdown.png、restart.png、suspend.png。这套调用方式在普通用户下会失败因为/sbin/shutdown需要root权限。很多课程设计到这里只点按钮看报错但设计报告里如果写实现关机功能就得处理权限问题常见的折中方案是让程序调用loginctl或systemctl命令或者在程序外部用sudo运行。这一点我们第4章继续展开。3. 源码包怎么用文件清单、pro配置与Ubuntu构建全流程拿到这个压缩包以后先别急着打开工程文件压缩包里的内容不只是源码还包括一份设计报告word和一堆截图。截图文件命名像图片4.png、图片13.png这类一看就是作者在做报告过程中从界面截的图不是运行必须的素材。真正参与编译的只有下表中列出的文件。3.1 拿到包先看什么文件清单与目录规划下面这张表是把压缩包内容按用途拆分后的核心清单文件作用是否参与编译TaskManager.proqmake工程文件定义模块、源文件和资源配置是main.cpp程序入口创建QApplication和MainWindow是mainwindow.h / mainwindow.cpp主窗口类声明与实现全部业务逻辑所在是mainwindow.ui主窗口界面布局控件命名和信号槽连接定义是icon.qrcQt资源文件声明程序用到的图标资源是images/*.png菜单栏图标和报告截图视位置而定设计报告.docx课程设计说明文档否README.md项目说明否注意images目录里既有按钮图标new.png、suspend.png、shutdown.png等也有报告截图图片N.png。图标通过icon.qrc引用如果换了目录相对路径找不到会导致编译时报RCC错误。README.md和.gitattributes只是说明性文件不影响构建。3.2 环境准备与.pro文件配置原项目给出的开发环境是Ubuntu 14.04和Qt Creator。我在新一点的发行版上复现时只装了qtbase5-dev和qt5-qmake就能正常编译代码没有明显依赖某个古老Qt版本的特性。下面这段TaskManager.pro是这类Qt Widgets项目最典型的配置QT core gui greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET TaskManager TEMPLATE app SOURCES main.cpp \ mainwindow.cpp HEADERS mainwindow.h FORMS mainwindow.ui RESOURCES icon.qrc第一行引入core和gui模块这是Qt Widgets应用的基础。第二行是一个版本判断如果Qt主版本大于4就把widgets模块加上。这个判断主要是为了兼容Qt 4也是Qt 5时代新建Widgets工程的标准模板。TARGET定义了生成的可执行文件名也就是编译完成后会得到一个名为TaskManager的可执行文件。TEMPLATE app表示生成应用程序而不是库。最后把cpp、h、ui、qrc文件都列进对应变量qmake会据此生成Makefile。如果是在命令行环境下编译需要先确认qmake在PATH里。可以用qmake -v查看版本。在Ubuntu上如果执行qmake报command not found说明qt5-qmake没装需要先补依赖。注意系统里可能同时存在qmake和qmake-qt5这两个命令后者是Qt 5专用。3.3 编译三步qmake、make、运行编译流程非常简单三步就能出可执行文件qmake TaskManager.pro make -j4 ./TaskManager第一步qmake读取工程文件生成Makefile。如果工程文件不在当前目录可以指定完整路径生成的Makefile默认落在当前目录。第二步make根据Makefile调编译器-j4表示四线程并行编译能把编译时间缩短到原来的三分之一左右。编译完成后目录下会多出一个TaskManager可执行文件。第三步直接运行程序会打开图形界面。如果用的是Qt Creator操作就更省事File - Open File or Project选中TaskManager.pro然后直接点左下角的绿色运行按钮。Qt Creator会先自动执行qmake再make编译输出和错误信息都显示在底部面板里。稍有经验的开发者会注意到打开.pro文件时Qt Creator会弹出一个配置Kit的对话框选择Desktop默认套件即可不建议选交叉编译套件因为它生成的程序跑不起来。常见编译错误是Project ERROR: Unknown module(s) in QT: widgets。这个报错说明系统的Qt版本是Qt 4或者QT widgets这一行没被正确识别。解决思路是确认qt5-qmake已安装并在.pro里保留greaterThan那一行判断。另一个高频错误是RCC资源文件找不到报错会指向icon.qrc里某个png路径不存在这时打开images目录对比文件名修正路径或文件名大小写因为Linux下文件名是大小写敏感的。4. 避坑清单Ubuntu Qt任务管理器最容易踩的六个坑代码能编译通过只是第一步真正跑起来才发现问题都在运行时。这一章是复现这套项目时我踩过、以及替别人排查过程中总结出的高频坑每条都按现象、原因、解决的顺序写。4.1 六个高频坑现象、原因、解决坑1运行新进程时路径带空格就启动失败。现象在弹窗里输入/home/test/My App/start.sh或者ls -l程序提示启动失败但在系统终端里同样命令能正常执行。原因QProcess::startDetached的单一字符串参数会被当成完整命令去解析路径中的空格会被拆断后面的参数也可能被误认为是可执行文件名的一部分。解决把命令字符串拆成程序路径和参数列表例如QProcess::startDetached(/bin/sh, QStringList() -c cmd);用/bin/sh -c包一层让shell自己去解析字符串里的空格和参数。我一般建议改成这种写法成本低且对用户输入更宽容。坑2进程信息页刷新太快导致界面卡顿、CPU占用拉高。现象程序运行几秒后窗口拖动明显变慢用系统监视器看到TaskManager进程本身占用了20%以上的CPU。原因作者如果直接把遍历/proc目录的代码放在主线程并且刷新周期设置成500ms甚至更短每次都要new大量QTableWidgetItem并设置文本UI线程被反复阻塞。解决把刷新间隔拉到2000ms以上并且只在进程页可见时才刷新切换页面时立即刷一次。代码层面可以用QTimer控制但注意定时器回调里也要判断当前停留在哪个Tab。坑3中文乱码界面显示一堆问号。现象系统信息页的标签、按钮上的中文文字在Ubuntu下显示为乱码尤其在非中文locale下更严重。原因源文件是GBK编码而Qt 5默认把源码字符串当成UTF-8处理两边不一致就会乱码。解决把mainwindow.cpp等源文件统一转成UTF-8编码代码里用QStringLiteral包装中文字面量。在Qt 5下QString::fromUtf8也能救但最好的解决办法是从源头统一编码。坑4菜单栏点关机、重启没反应或者提示权限不足。现象运行程序后点击关机系统弹出一个错误对话框或者干脆没反应程序也没有退出。原因普通用户没有权限直接执行/sbin/shutdown除非系统配置了sudo免密或者有polkit授权规则。解决课程设计场景下最简单的做法是调用loginctl命令例如loginctl poweroff对部分桌面环境有效另一条路子是给可执行文件配置sudo权限但不太推荐把系统命令权限放得这么宽。我一般会在设计报告里明确写模拟关机真正演示时只展示进程执行结果而不做实际关机。坑5CPU占用率显示为0、不变或者偶尔超过100%。现象性能页的CPU百分比一直不动或者跳出一个如356%这种不合理的数字。原因/proc/stat所有字段一次性读出来在单次采样里直接计算空闲和总时间的比值没有做差值多核环境下手动累加各核数值时又重复计入了总计数。解决回到2.2节的算法必须采集两次样本做差值。超过100%的情况通常是把CPU时间累加错了或者把每次采样时间内的总差值当成了单核时间核数N的计算要除以处理器个数。坑6改了mainwindow.ui重新运行还是旧界面。现象在UI设计器里拖了几个控件、改了容器布局点击运行后发现窗口还是老样子。原因Qt的UI文件需要先通过uic编译成ui_mainwindow.h再参与整个编译过程。有时IDE没有把ui文件标记为需要重新生成增量构建就跳过了这一步。解决在Qt Creator里执行Clean把构建目录下的Makefile和中间产物清掉再重新qmake和构建。如果不放心直接把整个build目录删掉重建这是最彻底的后悔药。4.2 定位问题的三板斧重跑qmake、清构建目录、打印/proc字段上面六个坑里有一半靠重新构建就能解决另一半要靠数据对比来定位。我的排查顺序是固定的先做构建层面的排除make clean rm -f Makefile qmake TaskManager.pro make -j4这样能排除moc和uic生成文件过期的因素。如果问题还在就进入数据层排查。比如CPU占用率不对直接把/proc/stat第一行打印出来看cat /proc/stat | head -n 1输出是类似cpu 231846 3488 78452 ...的一长串数字从第1个索引开始才是user、nice、system、idle。把原始字段和程序里解析出来的数据对比立刻就能看出是错位还是漏读。这个方法比自己瞎猜快得多也是我反复强调读/proc必须打印原始数据的原因。5. 二次开发刷新周期、进程过滤和图标替换怎么落地这个资源直接跑起来只是第一步绝大多数人拿到源码是要改的要么改界面要么加功能。这一章讲三个最常被改动的点都是我在实际二次开发中用过的路子。5.1 刷新周期在哪里调定时器、Tab切换、线程取舍原项目每次切换Tab才刷新一次数据所以进程列表不会自己更新。要让数据活起来常规做法是加一个QTimerQTimer *refreshTimer new QTimer(this); refreshTimer-setInterval(2000); connect(refreshTimer, QTimer::timeout, this, MainWindow::onRefreshTimeout); refreshTimer-start();setInterval(2000)表示每2秒触发一次onRefreshTimeout。业务逻辑不复杂时直接把onRefreshTimeout写成检索当前Tab并刷新void MainWindow::onRefreshTimeout() { if (m_isRefreshing) return; m_isRefreshing true; show_tab_info(ui-tabWidget-currentIndex()); m_isRefreshing false; }这里加了一个m_isRefreshing成员变量做防重入保护。因为刷新时间可能超过2秒如果上一次还没结束下一次又触发会叠加卡顿。这个标志位是关键很多人只加QTimer不加保护最后界面一样卡。如果进程数量大、机器性能弱刷新逻辑就要从主线程挪到后台线程。常见做法是开一个QThread或使用QtConcurrent跑采集函数主线程只负责收到信号后更新表格。要做这个改造核心是把show_process_info里读取/proc的部分拆成纯数据采集函数不碰任何UI把结果封装成结构体通过信号发回主线程。这个改造动静较大但对一个要长期运行的任务管理器是值得的。5.2 给进程列表增加过滤和排序默认进程页刷新时会把所有进程一股脑塞进表格。如果系统里开了几百个进程找目标进程全靠滚轮。最轻量的过滤方案是加一个QLineEdit每次文字变化时重新遍历表格并隐藏不匹配的行void MainWindow::on_lineEditFilter_textChanged(const QString keyword) { for (int row 0; row ui-tableProcess-rowCount(); row) { QTableWidgetItem *item ui-tableProcess-item(row, 1); bool match item-text().contains(keyword, Qt::CaseInsensitive); ui-tableProcess-setRowHidden(row, !match); } }这里约定第1列是进程名contains做模糊匹配第三个参数Qt::CaseInsensitive表示不区分大小写。注意如果表格开启setSortingEnabled(true)行号会在排序时被打乱setRowHidden用的是旧行号就会藏错行。我的实践经验是要么不开内置排序要么过滤后重新填充所有数据别混着用。这也是很多人改到一半翻车的地方。重填方案其实不复杂清空表格遍历进程集合只填充包含关键词的行最后按PID升序插一行排一行。优点是不会出现行号错位缺点是过滤条件变化时要整体重建不过以任务管理器的数据量来说完全够用。5.3 图标资源与qrc文件管理菜单栏的图标走Qt资源系统icon.qrc里声明了png文件路径。标准格式如下RCC qresource prefix/ fileimages/new.png/file fileimages/suspend.png/file fileimages/quit.png/file fileimages/shutdown.png/file fileimages/restart.png/file /qresource /RCCprefix写/表示资源根路径代码里加载图标时用:/images/new.png这种写法。qrc文件里每个file路径是相对qrc文件所在目录的所以工程文件里写的fileimages/new.png/file就表示qrc文件同目录下的images文件夹。资源被编译进可执行文件后程序运行时不需要再依赖图片文件。如果要换图标直接把新png覆盖到images目录下同名文件即可重新编译后生效。需要注意两点一是文件名大小写必须严格一致Linux编译器对资源路径大小写敏感二是png格式推荐jpg虽然也能编译进去但有透明背景需求的按钮图标jpg会显示白色底块。我一般会把按钮图标统一裁剪成16x16或24x24保证菜单栏显示效果紧凑。6. 用top对照验证这套任务管理器是否靠谱二次开发完成后不能只看界面好像能刷数据就交付。我自己常用的验证方法是用top命令做交叉对照因为任务管理器显示的数据和top读的是同一批/proc数据源差距如果在合理范围内就说明解析逻辑没问题。验证分三步走。第一步启动任务管理器同时打开系统终端执行top记录下top第一行显示的CPU占用率和内存占用。第二步制造压力负载让CPU使用率有明显变化while true; do echo -n /dev/null; done 这个死循环会持续占用一个CPU核心top里能看到某个核跑满。此时看任务管理器性能页的CPU百分比正常应该在50%左右浮动双核机器上占用一个核如果数值纹丝不动说明两次采样差值逻辑没生效。第三步批量制造进程验证进程页的实时性for i in $(seq 1 20); do sleep 30 done这个命令一次性拉起20个sleep进程切换任务管理器到进程信息页能看到20个sleep进程出现在表格里。接着等待它们自然退出再切换一次页面列表里的对应条目应该消失。如果任务管理器还带结束进程功能选中其中一个sleep进程序号进程消失且top里对应PID也消失说明进程管理链路是通的。除了对比数据我还会做一次长稳测试让任务管理器连续运行12小时每隔一小时手动切换一遍三个Tab看有没有内存持续上涨、UI失去响应、进程列表越刷越慢的情况。Qt程序最容易出这类累积性问题尤其是每2秒new一批QTableWidgetItem不回收的场景。最后要检查的是菜单项里的关机、重启这两个危险动作。我的习惯是在虚拟机上点一遍确认程序能正确调起系统级命令而不是只在真机上留着不敢点。从那以后我每次拿到这类Qt系统监控源码都强制先跑一遍top对照验证再做界面和权限检查最后才会去阅读和修改核心代码逻辑。顺序反过来的话改到一半经常分不清问题出在数据源、界面刷新还是权限隔离。希望帮到你。本文还有配套的精品资源点击获取