如何使用Qt/PySide2扫描范围内所有可用WiFi并连接指定网络
基于PySide2的WiFi扫描与连接实现方案
你当前使用的
QNetworkConfigurationManager仅能获取系统中已保存的网络配置记录,无法触发实时WiFi扫描,也无法返回未保存过的周边热点数据,因此会出现Discovered等状态无返回结果的问题。
Qt原生没有提供跨桌面平台的实时WiFi扫描接口,我们可以通过封装不同平台的系统命令,结合PySide2的信号槽机制实现需求,完整实现代码如下:
核心工具类实现
import subprocess import sys import os from PySide2.QtCore import QObject, Signal, Slot class WifiManager(QObject): # 扫描完成信号,返回格式:[(热点名, 信号强度, 加密类型), ...] scan_finished = Signal(list) # 连接完成信号,返回布尔值标识是否连接成功 connect_finished = Signal(bool) def __init__(self, parent=None): super().__init__(parent) @Slot() def start_scan(self): """启动实时WiFi扫描""" wifi_list = [] if sys.platform == "win32": # Windows平台调用netsh命令扫描 result = subprocess.run( ["netsh", "wlan", "show", "networks", "mode=Bssid"], capture_output=True, text=True, encoding="gbk" ) output = result.stdout current_ssid = "" current_auth = "" for line in output.split("\n"): line = line.strip() if line.startswith("SSID") and "BSSID" not in line: current_ssid = line.split(":", 1)[1].strip() elif line.startswith("身份验证"): current_auth = line.split(":", 1)[1].strip() elif line.startswith("信号"): signal = line.split(":", 1)[1].strip() # 去重,只保留每个SSID的第一条记录 if current_ssid and current_ssid not in [item[0] for item in wifi_list]: wifi_list.append((current_ssid, signal, current_auth)) elif sys.platform == "linux": # Linux平台调用iwlist扫描,需替换为你的无线网卡名(可通过iw dev查询) result = subprocess.run( ["iwlist", "wlan0", "scan"], capture_output=True, text=True ) output = result.stdout # 解析逻辑可参考Windows自行补充 elif sys.platform == "darwin": # Mac平台调用airport工具扫描 result = subprocess.run( ["/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport", "-s"], capture_output=True, text=True ) output = result.stdout # 解析逻辑可参考Windows自行补充 self.scan_finished.emit(wifi_list) @Slot(str, str) def connect_to_wifi(self, ssid: str, password: str): """连接指定SSID的WiFi,仅支持WPA2-PSK加密类型,其他类型可自行修改对应配置""" success = False if sys.platform == "win32": # 生成Windows WiFi配置文件 profile_content = f"""<?xml version="1.0"?> <WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1"> <name>{ssid}</name> <SSIDConfig> <SSID> <name>{ssid}</name> </SSID> </SSIDConfig> <connectionType>ESS</connectionType> <connectionMode>auto</connectionMode> <MSM> <security> <authEncryption> <authentication>WPA2PSK</authentication> <encryption>AES</encryption> <useOneX>false</useOneX> </authEncryption> <sharedKey> <keyType>passPhrase</keyType> <protected>false</protected> <keyMaterial>{password}</keyMaterial> </sharedKey> </security> </MSM> </WLANProfile>""" temp_file = f"{ssid}_wifi_profile.xml" with open(temp_file, "w", encoding="utf-8") as f: f.write(profile_content) # 添加WiFi配置到系统 subprocess.run( ["netsh", "wlan", "add", "profile", f"filename={temp_file}"], capture_output=True ) # 执行连接 res = subprocess.run( ["netsh", "wlan", "connect", f"name={ssid}"], capture_output=True ) success = res.returncode == 0 os.remove(temp_file) elif sys.platform == "linux": # Linux用NetworkManager的nmcli命令连接 res = subprocess.run( ["nmcli", "dev", "wifi", "connect", ssid, "password", password], capture_output=True ) success = res.returncode == 0 elif sys.platform == "darwin": # Mac用networksetup命令连接,en0为默认无线网卡名,可根据实际情况修改 res = subprocess.run( ["networksetup", "-setairportnetwork", "en0", ssid, password], capture_output=True ) success = res.returncode == 0 self.connect_finished.emit(success)
使用示例
from PySide2.QtWidgets import (QApplication, QWidget, QVBoxLayout, QPushButton, QListWidget, QInputDialog, QMessageBox, QLineEdit) if __name__ == "__main__": app = QApplication([]) window = QWidget() window.setWindowTitle("WiFi扫描工具") layout = QVBoxLayout(window) wifi_manager = WifiManager() wifi_list_widget = QListWidget() scan_btn = QPushButton("扫描周边WiFi") # 扫描结果回调 def handle_scan_result(wifi_list): wifi_list_widget.clear() for ssid, signal, auth in wifi_list: wifi_list_widget.addItem(f"热点名:{ssid} | 信号:{signal} | 加密:{auth}") # 点击列表项触发连接 def handle_item_clicked(item): ssid = item.text().split("|")[0].replace("热点名:", "").strip() password, ok = QInputDialog.getText(window, "连接WiFi", f"请输入{ssid}的密码:", echo=QLineEdit.Password) if ok and password: wifi_manager.connect_to_wifi(ssid, password) # 连接结果回调 def handle_connect_result(success): if success: QMessageBox.information(window, "提示", "连接成功") else: QMessageBox.warning(window, "提示", "连接失败,请检查密码或热点状态") scan_btn.clicked.connect(wifi_manager.start_scan) wifi_manager.scan_finished.connect(handle_scan_result) wifi_list_widget.itemClicked.connect(handle_item_clicked) wifi_manager.connect_finished.connect(handle_connect_result) layout.addWidget(scan_btn) layout.addWidget(wifi_list_widget) window.resize(600, 400) window.show() app.exec_()
注意事项
- Windows平台运行时需要以管理员身份启动程序,否则扫描结果可能不完整
- Linux平台需确保已安装
wireless-tools和network-manager包,无线网卡名称可通过iw dev命令查询替换代码中的wlan0 - 示例默认仅支持WPA2-PSK加密的热点,如需支持WEP、开放网络等类型,可修改对应平台的配置参数
内容的提问来源于stack exchange,提问作者Ahmad Mansoori
相关产品推荐
相关产品推荐

