简介这份资源面向在 Linux 平台从事桌面或嵌入式开发、希望掌握无线网络编程的开发者提供一套基于 QT 框架实现 Wi-Fi 扫描与连接的完整示例工程。项目以 Ubuntu 16.04 为运行环境围绕 QNetworkConfigurationManager 扫描热点、QNetworkConfiguration 激活网络、结合 wpa_supplicant 完成 WPA/WPA2 认证等核心流程展开并配套密码输入、连接与断开等交互界面帮助读者理解 QT 信号槽机制与 Linux 网络管理工具的协作方式。压缩包共 12 个文件约 9KB包含 4 个 cpp 源文件、3 个头文件、3 个 ui 界面文件以及 pro 工程文件和用户配置分别承载业务逻辑、类声明、界面布局与工程构建配置结构清晰便于二次开发。目前已有 4522 人学习下载适合作为 QT 网络编程入门练手或课程设计参考读者可从中获取扫描、连接、错误处理与 UI 状态更新的完整实现思路。1. 从命令行到图形界面一个 Linux 下 Qt 连接 WiFi 的小工具在 Linux 桌面环境里连 WiFi多数人第一反应是点右上角的网络图标或者敲nmcli。但如果你的设备跑的是定制系统、工控屏、嵌入式面板桌面环境可能压根没装网络管理器前端这时候一个能自己控制扫描、输入密码、发起连接的图形程序就很有用了。这份资源就是一个用 Qt 写的 Linux WiFi 连接程序核心思路是让 Qt 界面去调用系统里的无线工具把「扫描热点 → 选一个 → 输密码 → 连上」这条链路做成可视化操作。它适合正在做 Linux 桌面工具、嵌入式 HMI 面板或者想学 Qt 进程调用与网络状态解析的开发者。整套代码不依赖冷门库编译门槛低拿来当 Qt 与系统命令交互的练手项目正合适。2. 技术选型与整体架构Qt 界面怎么和系统无线工具对接2.1 为什么是 Qt 系统命令而不是纯 Qt 网络模块Qt 自带的QNetworkConfigurationManager在部分 Linux 发行版上对 WiFi 的支持并不完整尤其是精简系统里它拿不到扫描列表也发不起连接。所以这个项目走的是另一条路Qt 只负责界面和逻辑调度真正的扫描、连接动作交给系统里现成的无线命令行工具去执行程序读取命令输出再解析成结构化数据。常见做法是调用nmcliNetworkManager 的命令行客户端或iwlist/wpa_cli。nmcli的好处是输出格式稳定、支持连接和断开适合做完整流程iwlist更偏底层扫描适合只读场景。这个项目里我一般会优先用nmcli因为它一条命令就能完成扫描和连接省去自己拼wpa_supplicant.conf的麻烦。架构上分三层界面层QWidget或QMainWindow承载热点列表、密码输入框、连接按钮和状态提示。逻辑层封装一个 WiFi 管理类负责拼命令、启动QProcess、解析输出、发信号通知界面刷新。系统层nmcli或iwlist实际执行返回文本结果。这样分层的好处是哪天系统里换成了iw系列工具只需要改逻辑层里拼命令和解析的那几个函数界面层不用动。2.2 目录结构与关键文件职责一个典型的工程目录大概长这样wifi-qt/ ├── wifi-qt.pro # qmake 工程文件 ├── main.cpp # 程序入口 ├── mainwindow.h/.cpp # 主窗口界面与交互 ├── mainwindow.ui # Qt Designer 布局文件 ├── wifimanager.h/.cpp # 无线管理核心类 └── resources.qrc # 图标等资源wifimanager是整个项目的核心它对外暴露几个方法scan()扫描、connectTo(ssid, password)连接、disconnect()断开以及一个QListWifiInfo保存扫描结果。WifiInfo是个简单结构体存 SSID、信号强度、加密方式、是否已连接。mainwindow负责把wifimanager的信号接到界面槽函数上比如扫描完成后刷新QListWidget连接成功或失败后更新状态栏文字。2.3 用 QProcess 调命令并解析输出核心动作都靠QProcess完成。下面这段是扫描并解析nmcli输出的简化实现// wifimanager.cpp void WifiManager::scan() { // -t 表示 terse 模式字段用冒号分隔方便解析 // -f 指定只输出 SSID、信号强度、安全类型 QStringList args; args -t -f SSID,SIGNAL,SECURITY device wifi list; QProcess process; process.start(nmcli, args); process.waitForFinished(5000); // 最多等 5 秒避免界面卡死 if (process.exitStatus() ! QProcess::NormalExit) { emit scanFailed(nmcli 执行异常); return; } QString output QString::fromLocal8Bit(process.readAllStandardOutput()); QListWifiInfo list; const QStringList lines output.split(\n, Qt::SkipEmptyParts); for (const QString line : lines) { // terse 模式下字段用冒号分隔但 SSID 里可能含冒号需从右往左切 int lastColon line.lastIndexOf(:); int secondColon line.lastIndexOf(:, lastColon - 1); if (secondColon 0) continue; WifiInfo info; info.ssid line.left(secondColon); info.signal line.mid(secondColon 1, lastColon - secondColon - 1).toInt(); info.security line.mid(lastColon 1); if (!info.ssid.isEmpty()) list.append(info); } emit scanFinished(list); }逻辑说明nmcli -t的输出用冒号分隔字段但 SSID 本身可能包含冒号所以不能简单split(:)要从右往左定位最后两个冒号把 SSID 完整保留下来。waitForFinished给 5 秒超时是因为无线扫描有时会慢但也不能无限等否则界面像卡死一样。参数-f SSID,SIGNAL,SECURITY只取需要的三列减少解析负担。连接动作类似只是命令换成// 连接指定 SSID密码通过 nmcli 参数传入 QStringList args; args device wifi connect ssid; if (!password.isEmpty()) args password password; QProcess process; process.start(nmcli, args); process.waitForFinished(10000); bool ok (process.exitCode() 0); emit connectResult(ok, ok ? 连接成功 : process.readAllStandardError());这里exitCode() 0是判断成功的关键nmcli失败时会把原因写到标准错误直接读出来显示给用户比只弹一句「连接失败」有用得多。注意密码作为命令行参数传递在部分系统上可能被同机其他用户通过进程列表看到。如果安全要求高常见做法是改用nmcli的交互模式或wpa_cli的 stdin 输入避免密码出现在参数里。3. 从零跑起来编译环境、依赖与首次运行3.1 环境准备与依赖确认这套代码依赖 Qt 5 或 Qt 6 的基础模块QtWidgets、QtCore以及系统里的nmcli。在主流发行版上先确认这几样# 检查 Qt 版本qmake 能输出版本号说明 Qt 开发环境在 qmake --version # 检查 nmcli 是否可用 nmcli --version # 检查无线设备是否被识别 nmcli device status如果nmcli没装Debian/Ubuntu 系一般是network-manager包Fedora 系是NetworkManager。装好之后还要确保 NetworkManager 服务在跑否则命令会报「NetworkManager is not running」。Qt 开发环境如果缺Ubuntu 上可以装qtbase5-dev和qt5-qmake或者直接用 Qt 官方安装器装 Qt Creator。用 Qt Creator 打开.pro文件是最省事的路径它会自动识别套件。3.2 编译与运行步骤用 qmake 手动编译的流程# 进入工程目录 cd wifi-qt # 生成 Makefile qmake wifi-qt.pro # 编译-j4 表示四线程并行加快速度 make -j4 # 运行WiFi 操作通常需要权限普通用户可能读不到扫描结果 sudo ./wifi-qt如果编译报「找不到 QtWidgets」说明.pro里没写对模块检查是否有QT core gui widgets如果运行后扫描列表是空的先别怀疑代码直接在终端跑一遍nmcli -t -f SSID,SIGNAL,SECURITY device wifi list看有没有输出。命令本身没输出程序自然也没数据。3.3 界面交互流程与信号槽连接程序启动后主窗口一般有一个「扫描」按钮、一个热点列表、一个密码输入框和一个「连接」按钮。信号槽的连接大致是// mainwindow.cpp 构造函数里 connect(ui-scanButton, QPushButton::clicked, m_wifiManager, WifiManager::scan); connect(m_wifiManager, WifiManager::scanFinished, this, MainWindow::onScanFinished); connect(ui-connectButton, QPushButton::clicked, this, MainWindow::onConnectClicked);onScanFinished里清空列表再逐条添加每条显示 SSID 和信号强度。onConnectClicked从列表取当前选中项的 SSID从输入框取密码调connectTo。这样界面和逻辑完全解耦WifiManager不关心谁在调它。提示扫描是耗时操作如果直接在按钮槽里同步执行界面会短暂无响应。更稳妥的做法是把WifiManager放到独立线程或者至少用QProcess的异步信号finished代替waitForFinished让事件循环继续跑。4. 核心模块拆解扫描、连接与状态回传的完整链路4.1 扫描结果的数据结构与去重WifiInfo结构体建议包含这些字段struct WifiInfo { QString ssid; // 热点名称 int signal; // 信号强度 0-100 QString security; // 加密方式如 WPA2、WPA3、空表示开放 bool connected; // 是否当前已连接 };扫描结果里同一个 SSID 可能出现多次多 AP 同名常见做法是按 SSID 去重保留信号最强的那条。可以在scanFinished之前做一次合并QMapQString, WifiInfo merged; for (const WifiInfo info : list) { if (!merged.contains(info.ssid) || merged[info.ssid].signal info.signal) merged[info.ssid] info; } emit scanFinished(merged.values());这样列表不会出现一堆重复项用户体验干净很多。4.2 连接流程与密码处理连接时要注意几种情况开放网络不需要密码加密网络密码为空要直接提示用户已连接的网络重复点击要给出反馈。connectTo里可以先做参数校验void WifiManager::connectTo(const QString ssid, const QString password) { if (ssid.isEmpty()) { emit connectResult(false, 未选择热点); return; } // 加密网络但密码为空直接拦下不用去调 nmcli if (password.isEmpty() !isOpenNetwork(ssid)) { emit connectResult(false, 该网络需要密码); return; } // ... 拼 nmcli 命令并执行 }isOpenNetwork可以查扫描结果里该 SSID 的security字段是否为空。这样能省掉一次注定失败的进程调用反馈也更快。4.3 状态回传与界面刷新连接结果通过信号回传界面根据成功与否更新状态栏和按钮文字。一个容易忽略的点是nmcli连接成功后系统需要一点时间完成 DHCP 拿 IP如果立刻去查网络状态可能还是「未连接」。常见做法是连接成功后延迟一两秒再刷新状态或者监听nmcli monitor的输出。// 连接成功后延迟刷新给 DHCP 留时间 QTimer::singleShot(1500, this, [this]() { emit statusChanged(queryCurrentStatus()); });queryCurrentStatus可以调nmcli -t -f GENERAL.STATE device show wlan0之类的命令解析出当前是否已连接、连的是哪个 SSID。5. 避坑与常见问题权限、解析和跨发行版差异5.1 扫描列表为空或报权限错误现象程序运行后列表空白终端里手动跑nmcli却正常。原因图形程序从桌面启动时环境变量和权限上下文与终端不同某些系统下普通用户对无线设备没有扫描权限。解决确认程序以有权限的用户运行或者把用户加入netdev组实在不行用sudo启动测试。生产环境更稳妥的是通过 polkit 授权而不是直接提权。5.2 SSID 含冒号导致解析错位现象某些热点名称显示不全信号强度变成乱码。原因nmcli -t用冒号分隔字段SSID 里如果本身有冒号简单split就会切错。解决如 2.3 节所示从右往左定位最后两个冒号把左边整体当 SSID。这是这个项目里最容易翻车的一处血泪经验就是别偷懒用split。5.3 连接成功但界面仍显示未连接现象nmcli返回 0状态栏却还是「未连接」。原因DHCP 还没完成或者查询状态的命令查的是旧设备名。解决连接后延迟刷新并确认查询命令里的设备名如wlan0和实际一致。设备名在不同机器上可能是wlp3s0写死会出问题常见做法是用nmcli device status动态取无线设备名。5.4 不同发行版 nmcli 输出格式有差异现象在一台机器上解析正常换一台就错位。原因不同版本的 NetworkManagernmcli的字段顺序或 terse 模式行为可能微调。解决不要依赖字段位置尽量用-f明确指定字段名解析时按字段名匹配而不是按列号。如果条件允许锁定 NetworkManager 大版本减少兼容性意外。5.5 密码出现在进程列表现象连接时用ps能看到明文密码。原因密码作为命令行参数传给nmcli。解决改用nmcli的交互输入或把密码通过标准输入传给wpa_cli。对安全敏感的场景这一点不能省。6. 二次开发改造从能连到好用几个具体下手点拿到这套代码后最值得改的方向不是重写界面而是把「连接」这件事做得更稳、更可观测。我一般会先加一个连接状态轮询用QTimer每两秒查一次当前连接把结果同步到界面这样用户手动在系统里切了网络程序也能跟上。轮询命令用nmcli -t -f ACTIVE,SSID device wifi list解析出yes那条就是当前连接。第二个改造点是记住密码。Qt 里可以用QSettings把 SSID 和密码存到用户配置目录下次连接同一个热点自动填充。但要注意别明文存至少做个简单混淆或者接系统钥匙环。下面是个最小实现// 保存 QSettings settings(MyCompany, WifiQt); settings.setValue(wifi/ ssid /password, obfuscate(password)); // 读取 QString saved settings.value(wifi/ ssid /password).toString(); if (!saved.isEmpty()) ui-passwordEdit-setText(deobfuscate(saved));obfuscate只是简单异或别当成加密它的作用是避免密码以完全明文躺在配置文件里。真要安全还是走系统密钥环。第三个改造点是支持隐藏网络。nmcli扫描默认不显示隐藏 SSID需要手动指定。可以加一个「连接隐藏网络」的入口让用户手输 SSID 和密码直接调nmcli device wifi connect ssid password pwd hidden yes。第四个改造点是错误提示分级。现在失败只显示一行标准错误用户看不懂。可以按nmcli的返回内容做映射密码错误、超时、设备忙分别给不同提示。比如标准错误里含Secrets were required就是密码问题含Timeout就是超时。最后一个改造点是打包。用linuxdeployqt或直接写个.desktop文件把程序装到/usr/local/bin配上图标让它像系统自带工具一样能从应用菜单启动。.desktop里注意Exec路径和权限别让用户每次都得开终端。改完这些这个项目就从「能跑」变成「能日常用」了。我自己踩过的坑是一开始图省事把设备名写死成wlan0结果换到一台无线网卡命名规则不同的机器上直接连不上排查了半天才发现是设备名不对。从那以后我每次写涉及网络设备的代码都强制先跑一遍nmcli device status动态取名字再往下走。希望这些能帮到你少走点弯路。本文还有配套的精品资源点击获取