鸿蒙 WiFi 扫描流程(2)
接着上篇没有记录完的,我们继续梳理,需要上一篇做基础的请看:鸿蒙 WiFi 扫描流程(1)
上一篇我们讲到 scan_service.cpp 里面的 SingleScan 方法,继续这个方法往下看:
// foundation/communication/wifi/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_scan/scan_service.cppbool ScanService::SingleScan(ScanConfig &scanConfig)
{WIFI_LOGI("Enter ScanService::SingleScan.\n");#ifndef OHOS_ARCH_LITEif (!standByListerner.AllowScan()) {WIFI_LOGE("Scan not allowed when device in standby state.\n");return WIFI_OPT_FAILED;}
#endifGetAllowBandFreqsControlInfo(scanConfig.scanBand, scanConfig.scanFreqs); ---> 哪个Bandif ((scanConfig.scanBand == SCAN_BAND_UNSPECIFIED) && (scanConfig.scanFreqs.empty())) {WIFI_LOGE("Have no allowed band or freq.\n");return false;}InterScanConfig interConfig;interConfig.fullScanFlag = scanConfig.fullScanFlag;interConfig.hiddenNetworkSsid.assign(scanConfig.hiddenNetworkSsid.begin(), scanConfig.hiddenNetworkSsid.end());interConfig.scanStyle = scanConfig.scanStyle;/* Specified frequency */if (scanConfig.scanBand == SCAN_BAND_UNSPECIFIED) {interConfig.scanFreqs.assign(scanConfig.scanFreqs.begin(), scanConfig.scanFreqs.end());/** When band is SCAN_BAND_BOTH_WITH_DFS, need to scan all frequency,* scanFreqs can be empty.*/} else if (scanConfig.scanBand != SCAN_BAND_BOTH_WITH_DFS) {/* Converting frequency bands to frequencies. */if (!GetBandFreqs(scanConfig.scanBand, interConfig.scanFreqs)) {WIFI_LOGE("GetBandFreqs failed.\n");return false;}}/* Save the configuration. */int requestIndex = StoreRequestScanConfig(scanConfig, interConfig);if (requestIndex == MAX_SCAN_CONFIG_STORE_INDEX) {WIFI_LOGE("StoreRequestScanConfig failed.\n");return false;}std::unique_lock<std::mutex> lock(scanConfigMapMutex);if (pScanStateMachine == nullptr) {WIFI_LOGE("pScanStateMachine is null.\n");return false;}/* Construct a message. */ // 去状态机里面处理这个消息InternalMessage *interMessage =pScanStateMachine->CreateMessage(static_cast<int>(CMD_START_COMMON_SCAN), requestIndex);if (interMessage == nullptr) {scanConfigMap.erase(requestIndex);WIFI_LOGE("CreateMessage failed.\n");return false;}if (!AddScanMessageBody(interMessage, interConfig)) {scanConfigMap.erase(requestIndex);MessageManage::GetInstance().ReclaimMsg(interMessage);WIFI_LOGE("AddScanMessageBody failed.\n");return false;}pScanStateMachine->SendMessage(interMessage);return true;
}//foundation/communication/wifi/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_scan/scan_state_machine.cpp
bool ScanStateMachine::HardwareReady::ExecuteStateMsg(InternalMessage *msg)
{WIFI_LOGI("ScanStateMachine::HardwareReady::ExecuteStateMsg.\n");if (msg == nullptr) {WIFI_LOGE("msg is null.\n");return true;}switch (msg->GetMessageName()) {case CMD_START_COMMON_SCAN: ---》 处理这个消息pScanStateMachine->CommonScanRequestProcess(msg);return true;case CMD_START_PNO_SCAN:pScanStateMachine->PnoScanRequestProcess(msg);return true;default:return false;}
}
// 发起 CommonScanRequestProcess 流程
void ScanStateMachine::CommonScanRequestProcess(InternalMessage *interMessage)
{WIFI_LOGI("ScanStateMachine::CommonScanRequestProcess.\n");int requestIndex = 0;InterScanConfig scanConfig;if (!GetCommonScanRequestInfo(interMessage, requestIndex, scanConfig)) {ReportCommonScanFailed(requestIndex);return;}if (!VerifyScanStyle(scanConfig.scanStyle)) {WIFI_LOGE("invalid scan type");return;}{std::unique_lock<std::shared_mutex> guard(lock);waitingScans.insert(std::pair<int, InterScanConfig>(requestIndex, scanConfig));}StartNewCommonScan();
}void ScanStateMachine::StartNewCommonScan()
{WIFI_LOGI("Enter ScanStateMachine::StartNewCommonScan.\n");{std::shared_lock<std::shared_mutex> guard(lock);if (waitingScans.size() == 0) {ContinuePnoScanProcess();return;}ClearRunningScanSettings();bool hasFullScan = false;/* Traverse the request list and combine parameters */std::map<int, InterScanConfig>::iterator configIter = waitingScans.begin();for (; configIter != waitingScans.end(); ++configIter) {runningScanSettings.scanStyle = MergeScanStyle(runningScanSettings.scanStyle, configIter->second.scanStyle);std::vector<std::string>::iterator hiddenIter = configIter->second.hiddenNetworkSsid.begin();/* Remove duplicate hidden list */for (; hiddenIter != configIter->second.hiddenNetworkSsid.end(); ++hiddenIter) {if (std::find(runningScanSettings.hiddenNetworkSsid.begin(),runningScanSettings.hiddenNetworkSsid.end(),*hiddenIter) != runningScanSettings.hiddenNetworkSsid.end()) {continue;}runningScanSettings.hiddenNetworkSsid.push_back(*hiddenIter);}if (!hasFullScan) {/* When scanFreqs is empty, it means that scan all frequenties */if (configIter->second.scanFreqs.empty()) {runningScanSettings.scanFreqs.clear();runningFullScanFlag = true;hasFullScan = true;} else {std::vector<int>::iterator freqIter = configIter->second.scanFreqs.begin();/* Repetitions are eliminated */for (; freqIter != configIter->second.scanFreqs.end(); ++freqIter) {if (std::find(runningScanSettings.scanFreqs.begin(),runningScanSettings.scanFreqs.end(),*freqIter) != runningScanSettings.scanFreqs.end()) {continue;}runningScanSettings.scanFreqs.push_back(*freqIter);}}}}}if (!StartSingleCommonScan(runningScanSettings)) { ---》 继续看这个ReportCommonScanFailedAndClear(false);ContinuePnoScanProcess();return;}std::unique_lock<std::shared_mutex> guard(lock);runningScans.swap(waitingScans);waitingScans.clear();SwitchState(commonScanningState);WIFI_LOGI("StartNewCommonScan success.\n");
}bool ScanStateMachine::StartSingleCommonScan(WifiScanParam &scanParam)
{WIFI_LOGI("Enter ScanStateMachine::StartSingleCommonScan.\n");for (auto freqIter = scanParam.scanFreqs.begin(); freqIter != scanParam.scanFreqs.end(); ++freqIter) {WIFI_LOGI("freq is %{public}d.\n", *freqIter);}for (auto hiddenIter = scanParam.hiddenNetworkSsid.begin(); hiddenIter != scanParam.hiddenNetworkSsid.end();++hiddenIter) {WIFI_LOGI("hidden ssid is %{public}s.\n", SsidAnonymize(*hiddenIter).c_str());}WIFI_LOGI("Begin call Scan.\n");WifiErrorNo ret = WifiStaHalInterface::GetInstance().Scan(scanParam); ---> 是不是很熟悉,要通过idl_clientif ((ret != WIFI_IDL_OPT_OK) && (ret != WIFI_IDL_OPT_SCAN_BUSY)) {WIFI_LOGE("WifiStaHalInterface::GetInstance().scan failed.");return false;}WIFI_LOGI("End call Scan.\n");/** Start the timer. If no result is returned for a long time, the scanning* fails*/StartTimer(static_cast<int>(WAIT_SCAN_RESULT_TIMER), MAX_WAIT_SCAN_RESULT_TIME);return true;
}
继续看下代码,WifiStaHalInterface里面的 Scan 方法:
foundation/communication/wifi/wifi/services/wifi_standard/wifi_framework/wifi_manage/idl_client/wifi_sta_hal_interface.cpp
WifiErrorNo WifiStaHalInterface::Scan(const WifiScanParam &scanParam)
{CHECK_NULL_AND_RETURN(mIdlClient, WIFI_IDL_OPT_FAILED);return mIdlClient->Scan(scanParam);
}// foundation/communication/wifi/wifi/services/wifi_standard/wifi_framework/wifi_manage/idl_client/wifi_idl_client.cpp
WifiErrorNo WifiIdlClient::Scan(const WifiScanParam &scanParam)
{CHECK_CLIENT_NOT_NULL;ScanSettings settings;if (memset_s(&settings, sizeof(settings), 0, sizeof(settings)) != EOK) {return WIFI_IDL_OPT_FAILED;}bool bfail = false;do {if (scanParam.hiddenNetworkSsid.size() > 0) {settings.hiddenSsidSize = scanParam.hiddenNetworkSsid.size();settings.hiddenSsid = ConVectorToCArrayString(scanParam.hiddenNetworkSsid);if (settings.hiddenSsid == nullptr) {bfail = true;break;}}if (scanParam.scanFreqs.size() > 0) {settings.freqSize = scanParam.scanFreqs.size();settings.freqs = (int *)calloc(settings.freqSize, sizeof(int));if (settings.freqs == nullptr) {bfail = true;break;}for (int i = 0; i < settings.freqSize; ++i) {settings.freqs[i] = scanParam.scanFreqs[i];}}if (scanParam.scanStyle > 0) {settings.scanStyle = scanParam.scanStyle;}} while (0);WifiErrorNo err = WIFI_IDL_OPT_FAILED;if (!bfail) {err = StartScan(&settings); ---> 向 Hal 发起扫描}if (settings.freqs != nullptr) {free(settings.freqs);settings.freqs = nullptr;}if (settings.hiddenSsid != nullptr) {for (int i = 0; i < settings.hiddenSsidSize; ++i) {free(settings.hiddenSsid[i]);settings.hiddenSsid[i] = nullptr;}free(settings.hiddenSsid);settings.hiddenSsid = nullptr;}return err;
}
// 中间省略了 RPC的调用,代码可以看wifi_hal_crpc_server类,处理客户端请求,然后根据请求找到对应的函数,在调用HAL的方法
// 直接看调用的HAL方法:foundation/communication/wifi/wifi/services/wifi_standard/wifi_hal/wifi_hal_sta_interface.c
WifiErrorNo StartScan(const ScanSettings *settings)
{LOGD("Ready to start scan with param.");
#ifdef HDI_INTERFACE_SUPPORTint ret = HdiStartScan(settings);
#elseWifiWpaStaInterface *pStaIfc = GetWifiStaInterface(0);if (pStaIfc == NULL) {return WIFI_HAL_SUPPLICANT_NOT_INIT;}int ret = pStaIfc->wpaCliCmdScan(pStaIfc, settings); ---> 往supplicant 发送命令if (ret < 0) {LOGE("StartScan failed! ret=%{public}d", ret);return WIFI_HAL_FAILED;}
#endifif (ret == WIFI_HAL_SCAN_BUSY) {LOGD("StartScan return scan busy");return WIFI_HAL_SCAN_BUSY;}LOGD("StartScan successfully!");return WIFI_HAL_SUCCESS;
}// int (*wpaCliCmdScan)(WifiWpaStaInterface *p, const ScanSettings *settings);
// wifi/services/wifi_standard/wifi_hal/wifi_hal_module/wpa_supplicant_hal/wpa_sta_hal/wifi_supplicant_hal.c
static int WpaCliCmdScan(WifiWpaStaInterface *this, const ScanSettings *settings)
{if (this == NULL) {LOGE("WpaCliCmdScan, this is NULL!");return -1;}/* Invalidate expired scan results */WpaCliCmdBssFlush(this);unsigned len = CMD_BUFFER_SIZE;unsigned expectedLen = 0;if (settings != NULL) {expectedLen = AssignCmdLen(this, settings);}if (expectedLen >= len) {len = expectedLen + 1;}char *pcmd = (char *)calloc(len, sizeof(char));if (pcmd == NULL) {LOGE("WpaCliCmdScan, pcmd is NULL!");return -1;}int pos = 0;int res = 0;if (settings != NULL) {if (settings->scanStyle == SCAN_TYPE_PNO && settings->isStartPnoScan) {res = snprintf_s(pcmd, len, len - 1, "IFNAME=%s set pno 1", this->ifname);} else if (settings->scanStyle == SCAN_TYPE_PNO && !settings->isStartPnoScan) {res = snprintf_s(pcmd, len, len - 1, "IFNAME=%s set pno 0", this->ifname);} else {res = snprintf_s(pcmd, len, len - 1, "IFNAME=%s SCAN", this->ifname);}}if (res < 0) {LOGE("WpaCliCmdScan, snprintf_s error!");free(pcmd);return -1;}pos += res;if (settings != NULL && ConcatScanSetting(settings, pcmd + pos, len - pos) < 0) {LOGE("snprintf scan settings error");free(pcmd);return -1;}char buf[REPLY_BUF_SMALL_LENGTH] = {0};if (WpaCliCmd(pcmd, buf, sizeof(buf)) != 0) { ---》 命令下发free(pcmd);return -1;}free(pcmd);if (strncmp(buf, "FAIL-BUSY", strlen("FAIL-BUSY")) == 0) {LOGE("WpaCliCmdScan, WpaCliCmd return FAIL-BUSY!");return FAIL_BUSY;}return 0;
}// foundation/communication/wifi/wifi/services/wifi_standard/wifi_hal/wifi_hal_module/wpa_supplicant_hal/wifi_wpa_common.c
int WpaCliCmd(const char *cmd, char *buf, size_t bufLen)
{if (cmd == NULL || buf == NULL || bufLen <= 0) {LOGE("WpaCliCmd, invalid parameters!");return -1;}WpaCtrl *ctrl = GetWpaCtrl();if (ctrl == NULL || ctrl->pSend == NULL) {LOGE("WpaCliCmd, ctrl/ctrl->pSend is NULL!");return -1;}size_t len = bufLen - 1;LOGI("wpa_ctrl_request -> cmd: %{private}s", cmd);int ret = wpa_ctrl_request(ctrl->pSend, cmd, strlen(cmd), buf, &len, NULL);if (ret == WPA_CMD_RETURN_TIMEOUT) {LOGE("[%{private}s] command timed out.", cmd);return WPA_CMD_RETURN_TIMEOUT;} else if (ret < 0) {LOGE("[%{private}s] command failed.", cmd);return -1;}buf[len] = '\0';LOGI("wpa_ctrl_request -> buf: %{private}s", buf);if (strncmp(buf, "FAIL\n", strlen("FAIL\n")) == 0 ||strncmp(buf, "UNKNOWN COMMAND\n", strlen("UNKNOWN COMMAND\n")) == 0) {LOGE("%{private}s request success, but response %{public}s", cmd, buf);return -1;}return 0;
}
到此我们就看到命令发送到wpa,然后wpa 收到命令后去做扫描动作,那扫描到结果如何通知上层呢?下一篇我们继续记录。
相关文章:
鸿蒙 WiFi 扫描流程(2)
接着上篇没有记录完的,我们继续梳理,需要上一篇做基础的请看:鸿蒙 WiFi 扫描流程(1) 上一篇我们讲到 scan_service.cpp 里面的 SingleScan 方法,继续这个方法往下看: // foundation/communicat…...
微信小程序(四十)API的封装与调用
注释很详细,直接上代码 上一篇 新增内容: 1.在单独的js文件中写js接口 2.以注册为全局wx的方式调用接口 源码: utils/testAPI.js const testAPI{/*** * param {*} title */simpleToast(title提示){//可传参,默认为‘提示’wx.sho…...
WebSocket+Http实现功能加成
WebSocketHttp实现功能加成 前言 首先,WebSocket和HTTP是两种不同的协议,它们在设计和用途上有一些显著的区别。以下是它们的主要特点和区别: HTTP (HyperText Transfer Protocol): 请求-响应模型: HTTP 是基于请求-响应模型的协…...
go语言实现LRU缓存
go语言实现LRU Cache 题目描述详细代码 题目描述 设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最…...
git的奇特知识点
展示帮助信息 git help -gThe common Git guides are:attributes Defining attributes per pathcli Git command-line interface and conventionscore-tutorial A Git core tutorial for developerscvs-migration Git for CVS usersdiff…...
按键扫描16Hz-单片机通用模板
按键扫描16Hz-单片机通用模板 一、按键扫描的原理1、直接检测高低电平类型2、矩阵扫描类型3、ADC检测类型二、key.c的实现1、void keyScan(void) 按键扫描函数①void FHiKey(void) 按键按下功能②void FSameKey(void) 按键长按功能③void FLowKey(void) 按键释放功能三、key.h的…...
在容器镜像中为了安全为什么要删除 setuid 和 setgid?
在容器镜像中删除 setuid(set user ID)和 setgid(set group ID)权限通常是出于安全考虑。这两个权限位允许进程在执行时以文件所有者或文件所属组的身份运行,而不是以调用进程的用户身份运行。 删除 setuid 和 setgid…...
Flink 动态表 (Dynamic Table) 解读
博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,…...
【原创 附源码】Flutter海外登录--Google登录最详细流程
最近接触了几个海外登录的平台,踩了很多坑,也总结了很多东西,决定记录下来给路过的兄弟坐个参考,也留着以后留着回顾。更新时间为2024年2月8日,后续集成方式可能会有变动,所以目前的集成流程仅供参考&#…...
第70讲axios后端请求工具类封装
axios工具类封装: // 引入axios import axios from axios;// 创建axios实例 const httpService axios.create({// url前缀-http:xxx.xxx// baseURL: process.env.BASE_API, // 需自定义baseURL:http://localhost:80/,// 请求超时时间timeout: 3000 // 需自定义 })…...
【数学建模】【2024年】【第40届】【MCM/ICM】【F题 减少非法野生动物贸易】【解题思路】
一、题目 (一) 赛题原文 2024 ICM Problem F: Reducing Illegal Wildlife Trade Illegal wildlife trade negatively impacts our environment and threatens global biodiversity. It is estimated to involve up to 26.5 billion US dollars per y…...
第3节、电机定速转动【51单片机+L298N步进电机系列教程】
↑↑↑点击上方【目录】,查看本系列全部文章 摘要:本节介绍用定时器定时的方式,精准控制脉冲时间,从而控制步进电机速度。 一、计算过程 电机每一步的角速度等于走这一步所花费的时间,走一步角度等于步距角ÿ…...
【51单片机】LCD1602(可视化液晶屏)调试工具的使用
前言 大家好吖,欢迎来到 YY 滴 单片机系列 ,热烈欢迎! 本章主要内容面向接触过单片机的老铁 主要内容含: 欢迎订阅 YY滴C专栏!更多干货持续更新!以下是传送门! YY的《C》专栏YY的《C11》专栏YY…...
Netty应用(四) 之 Reactor模型 零拷贝
目录 6.Reactor模型 6.1 单线程Reactor 6.2 主从多线程Reactor (主--->Boss | 从--->Worker | 一主多从机制) 7.扩展与补充 8.Reactor模型的实现 8.1 多线程Reactor模型的实现(一个Boss线程,一个Worker线程) 8.2 多线程Reactor模…...
Huggingface上传模型
Huggingface上传自己的模型 参考 https://juejin.cn/post/7081452948550746148https://huggingface.co/blog/password-git-deprecationAdding your model to the Hugging Face Hub, huggingface.co/docs/hub/ad…Welcome,huggingface.co/welcome三句指…...
kyuubi 接入starrocks | doris
kyuubi 接入starrocks 一、环境 Hadoop集群 组件版本Hadoop3.1.1spark3.Xzookeeper3.XHive3.X kyuubi 版本 1.7.1 starrocks 2.X 已将kyuubi部署到yarn上,并且接入了spark3引擎,并通过Ambari进行kyuubi组件的管理,下面步骤为新增对sta…...
notepad++成功安装后默认显示英文怎么设置中文界面?
前几天使用电脑华为管家清理电脑后,发现一直使用的notepad软件变回了英文界面,跟刚成功安装的时候一样,那么应该怎么设置为中文界面呢?具体操作如下: 1、打开notepad软件,点击菜单栏“Settings – Prefere…...
HiveSQL——连续增长问题
注:参考文章: SQL连续增长问题--HQL面试题35_sql判断一个列是否连续增长-CSDN博客文章浏览阅读2.6k次,点赞6次,收藏30次。目录0 需求分析1 数据准备3 小结0 需求分析假设我们有一张订单表shop_order shop_id,order_id,order_time…...
使用cocos2d-console初始化一个项目
先下载好cocos2d-x的源码包 地址 https://www.cocos.com/cocos2dx-download 这里使用的版本是 自己的电脑要先装好python27 用python安装cocos2d-console 看到项目中有个setup.py的一个文件 python setup.py 用上面的命令执行一下。 如果执行正常的话回出现上面的图 然后…...
VitePress-13- 配置-title的作用详解
作用描述 1、title 是当前站点的标题;2、默认值是 :VitePress;3、当使用默认主题时,会直接展示在 页面的【导航条】中;4、一个特殊的作用 : 会作为单个页面的默认标题后缀!除非又指定了【title…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
