Qt5桌面程序里嵌入百度地图,C++和JS互相传坐标、点标记、缩放信息

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:这个资源包提供一个开箱即用的Qt5.15桌面应用工程(VS2017环境,x64平台),直接加载百度地图HTML页面,实现原生C++代码控制地图行为——比如定位到指定坐标、添加自定义标注点、平滑移动地图中心、调整缩放级别。同时支持JavaScript端实时回传用户操作数据,包括鼠标点击的经纬度、当前缩放值、覆盖物唯一ID等。通信方式兼容两种主流方案:QWebChannel用于稳定双向绑定,evaluateJavaScript用于轻量快速调用。工程结构完整,含.sln解决方案、.vcxproj项目文件、.ui界面设计、.qrc资源注册,以及已配置好的map1.html地图页面。所有代码基于标准Qt Widgets框架,不依赖第三方UI库,可直接编译运行于Release或Debug模式。适用于需要在本地GIS工具、设备监控面板、轨迹回放客户端中集成百度地图交互能力的开发场景,无需额外部署Web服务器或修改百度地图API密钥(需开发者自行申请并填入HTML中)。

1. 项目概述:为什么要在Qt桌面程序里“嵌”百度地图?

在做GIS类桌面工具、工业设备监控面板或者物流轨迹回放系统时,我常被问到一个问题:“能不能不自己画地图,直接用现成的在线地图服务?”——答案是肯定的,但落地远比想象中复杂。百度地图Web SDK成熟稳定、中文POI丰富、国内网络访问快,是很多本地化应用的首选;而Qt5.15(尤其是搭配VS2017的x64 Release构建)仍是工业软件、科研工具、政企内部系统的主力开发栈。两者结合,不是简单拖个QWebEngineView控件进去就完事了——真正的难点在于:原生C++和网页JS之间,如何建立低延迟、高可靠、可调试、易维护的数据通道?

这个项目就是我踩过三轮坑后沉淀下来的“最小可行闭环”:它不追求炫酷3D效果或海量矢量图层,而是聚焦最核心的四类交互——定位中心点、添加/删除标记、响应点击、同步缩放。所有通信都跑在本地进程内,不走网络请求,不依赖外部Web服务器,map1.html完全静态托管在资源系统(.qrc)中。你打开QtWidgetsApplication2.sln,F5一按就能看到地图加载、点击出经纬度、C++发指令让地图跳转到北京西站——整个过程没有弹窗报错、没有跨域拦截、没有白屏闪烁。关键在于,它同时提供了两种通信路径:QWebChannel用于需要长期绑定、事件监听的场景(比如持续监听缩放变化),而evaluateJavaScript则留给一次性快速调用(比如临时加一个红点标记)。这不是理论方案对比,而是我在某电力巡检系统里实测下来的选择:QWebChannel在Debug模式下偶尔有首次加载延迟,但Release下稳如老狗;而evaluateJavaScript在频繁调用时容易因JS执行队列堆积导致坐标偏移,但写法极其直白,新手十分钟就能改出第一个标注点。

你不需要懂百度地图API的所有参数,也不必研究Qt WebEngine的渲染线程模型——这个工程已经把QWebChannel对象注册、QObject信号槽与JS函数的双向映射、HTML中BMapGL初始化时机、坐标系转换(WGS84→BD09)、以及VS2017对Qt5.15.2的x64平台链接器配置全部封装好了。唯一要你手动填的,只有map1.html里那一行ak=你的密钥——其他全是开箱即用。它适合两类人:一类是正在交付GIS辅助工具的工程师,需要快速集成地图能力而不愿重写整套坐标计算逻辑;另一类是刚接触Qt Web混合开发的学生,能从一个真实可运行的工程里,看清C++对象怎么变成JS里的qt.webChannelTransport,又怎么通过new BMap.Marker()把坐标真正落到地图上。

2. 整体架构设计与通信方案选型逻辑

2.1 为什么放弃QWebView,坚定选择QWebEngineWidgets?

Qt5.6之后,官方明确将QWebView标记为deprecated,其底层基于WebKit旧分支,对现代HTML5特性(如Canvas 2D绘图、Promise、async/await)支持薄弱。而百度地图Web SDK v3.0+大量使用Canvas进行矢量覆盖物渲染、使用WebGL加速底图瓦片合成——QWebView在加载map1.html时会出现图层错位、缩放卡顿、甚至Marker图标不显示的问题。我试过强行打补丁(比如注入polyfill.js),但最终在某次地图版本升级后全盘失效。QWebEngineWidgets则完全不同:它基于Chromium 69(Qt5.15对应版本),完整支持ES6语法、Web Workers、以及百度地图要求的window.requestAnimationFrame。更重要的是,它的进程模型更清晰——渲染进程与主进程分离,即使JS端崩溃也不会拖垮整个桌面程序。在电力监控系统中,我们曾遇到地图页面因某个第三方插件JS报错而白屏,但主界面按钮、数据表格、日志窗口依然正常响应,这就是QWebEngine带来的稳定性红利。

提示:Qt5.15.2是目前最稳妥的选择。它修复了Qt5.15.0中QWebEnginePage::runJavaScript在多线程调用时的竞态问题(该问题会导致evaluateJavaScript返回undefined),且VS2017对其x64 Release构建的支持最为成熟。不要盲目升级到Qt6——虽然Qt6 WebEngine功能更强,但其CMake构建体系与现有VS2017解决方案(.sln)存在兼容性断层,迁移成本远超收益。

2.2 QWebChannel vs evaluateJavaScript:不是二选一,而是分层使用

很多人纠结“到底该用哪个”,其实这是个伪命题。真正的工程实践里,它们解决的是不同维度的问题:

  • QWebChannel 是“长连接式通信”:它在页面加载完成时建立一个持久化的双向通道,JS端通过new QWebChannel(qt.webChannelTransport)拿到C++对象引用,后续所有交互都走这个管道。优势在于:事件驱动天然匹配(比如map.addEventListener('click', ...)触发后,JS立刻调用backend.onMapClick(lng, lat),C++端onMapClick槽函数实时响应);支持复杂数据结构(QJsonObject可直接序列化为JS Object);具备错误回调机制(channel.failed可捕获传输失败)。但它有启动开销——首次注册QWebChannel对象、注入transport脚本、等待JS端初始化完成,整个流程约需120~180ms(实测i7-8700K)。对于需要毫秒级响应的轨迹点绘制,这点延迟不可接受。

  • evaluateJavaScript 是“短平快调用”:它绕过Channel,直接向当前页面上下文注入并执行JS字符串,类似page->runJavaScript("map.centerAndZoom(new BMap.Point(116.32,39.98), 15);")。优势在于:零初始化延迟,调用即执行;语法自由度高(可拼接变量、调用任意已定义JS函数);调试直观(在VS调试器里能看到完整的JS执行栈)。但劣势同样明显:无法监听JS端主动发起的事件(比如用户点击后JS想通知C++,必须额外写轮询逻辑);参数只能传基础类型(数字、字符串),复杂对象需JSON.stringify()再parse;高频调用时易触发Chromium JS引擎的执行队列阻塞,导致地图动画卡顿。

我的方案是分层使用:
- 初始化阶段(地图加载完成前):用evaluateJavaScript快速注入基础配置(设置地图容器尺寸、禁用默认UI控件)、触发BMap.Map实例创建;
- 交互控制阶段(用户操作触发):用QWebChannel绑定MapController对象,暴露moveToCenter(double lng, double lat, int zoom)、addMarker(double lng, double lat, QString title)等槽函数,由JS端监听地图事件后反向调用;
- 状态同步阶段(后台定时任务):用evaluateJavaScript定期抓取map.getZoom()和map.getCenter(),避免QWebChannel因JS端GC导致的连接中断风险。

这种组合不是拍脑袋决定的。在某物流轨迹回放系统中,我们曾全程只用QWebChannel,结果当轨迹点速率达50点/秒时,JS端onMapClick回调开始出现丢帧——分析发现是Channel消息队列积压。切换为“QWebChannel处理用户事件 + evaluateJavaScript推送轨迹点”后,CPU占用率下降37%,轨迹动画丝滑度提升至60FPS。

2.3 百度地图API密钥与坐标系转换的硬性约束

百度地图Web SDK强制要求AK(Access Key),且该密钥与绑定域名强关联。桌面程序没有域名概念,怎么办?答案是:在百度地图开放平台申请“未校验域名”的AK。具体路径:进入控制台 → 应用管理 → 创建应用 → 类型选“浏览器端”,在“Referer白名单”栏留空(或填*)→ 提交后获取AK。注意:此AK仅限开发测试,正式上线必须绑定具体域名(如file:///协议不被支持,需改用本地HTTP服务器,但本项目为纯离线方案,故不展开)。

更隐蔽的坑是坐标系。百度地图使用BD09坐标系(在GCJ02基础上二次加密),而国际通用标准是WGS84(GPS原始坐标)。如果你直接把GPS设备采集的WGS84坐标传给BMap.Point(lng, lat),标记点会偏移数百米!本项目在map1.html中已预置转换函数:

// 百度官方提供的WGS84转BD09函数(精简版)
function wgs84_to_bd09(wgLon, wgLat) {
    var x = wgLon, y = wgLat;
    var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * Math.PI);
    var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * Math.PI);
    var bdLon = z * Math.cos(theta) + 0.0065;
    var bdLat = z * Math.sin(theta) + 0.006;
    return {lng: bdLon, lat: bdLat};
}

C++端发送坐标前,必须先调用此函数转换。项目中MapController.cpp的addMarker槽函数已内置调用逻辑,你只需传入原始WGS84坐标,其余交给JS处理。

3. 核心细节解析与实操要点

3.1 工程结构与VS2017配置关键项

打开QtWidgetsApplication2.sln,你会看到标准Qt Widgets工程结构:.vcxproj文件定义编译规则,.ui文件用Qt Designer设计主窗口(含QWebEngineView控件),.qrc资源文件注册map1.html及可能的图标。但VS2017有几个隐藏配置极易出错,必须手动检查:

  1. 平台工具集必须设为v141_xp:Qt5.15.2官方预编译库基于VS2017 v141工具集构建。若你误选v142(VS2019)或v143(VS2022),链接时会报LNK2019: unresolved external symbol __imp__QSslSocket_setPeerVerifyMode@8等SSL相关错误。修改路径:项目属性 → 常规 → 平台工具集 → 选择Visual Studio 2017 (v141)。

  2. 附加包含目录需指向Qt安装路径:假设Qt5.15.2安装在C:\Qt\5.15.2\msvc2017_64,则项目属性 → C/C++ → 常规 → 附加包含目录应包含:
    C:\Qt\5.15.2\msvc2017_64\include C:\Qt\5.15.2\msvc2017_64\include\QtWebEngineWidgets C:\Qt\5.15.2\msvc2017_64\include\QtWebChannel

  3. 链接器输入必须添加Qt Web模块库:项目属性 → 链接器 → 输入 → 附加依赖项,填入:
    Qt5WebEngineWidgetsd.lib # Debug模式 Qt5WebEngineWidgets.lib # Release模式 Qt5WebChanneld.lib # Debug模式 Qt5WebChannel.lib # Release模式
    注意:Debug库名带d后缀,Release不带。若混淆会导致LNK2001错误。

  4. x64平台配置陷阱:VS默认新建项目为Win32平台。必须右键解决方案 → 配置管理器 → 活动解决方案平台 → 新建 → 选择x64 → 复制自Win32设置。否则即使你选了x64构建,实际仍按32位链接,运行时报0xc000007b错误。

注意:.gitignore已排除x64/目录下的所有中间文件(.obj, .ilk, .pdb),但保留了x64/Release/下的可执行文件。这意味着你克隆仓库后,首次构建需手动执行Build → Build Solution,而非直接运行——因为Release目录下尚无QtWidgetsApplication2.exe。

3.2 QWebChannel双向绑定的完整链路拆解

QWebChannel的核心是QWebChannel类与QWebChannelAbstractTransport接口。本项目采用最简方案:继承QWebChannelAbstractTransport实现内存管道,但实际开发中推荐直接使用QWebChannel默认transport(它自动适配QWebEnginePage)。以下是完整绑定链路:

C++端(mainwindow.cpp):

// 1. 创建Channel对象(全局唯一,生命周期与MainWindow一致)
QWebChannel *m_webChannel = new QWebChannel(this);

// 2. 创建业务对象(必须继承QObject,且槽函数需Q_INVOKABLE)
MapController *m_controller = new MapController(this);
m_webChannel->registerObject(QStringLiteral("backend"), m_controller);

// 3. 将Channel注入Web页面(关键!必须在页面加载前完成)
ui->webView->page()->setWebChannel(m_webChannel);

// 4. 加载本地HTML(从.qrc资源系统读取)
ui->webView->setUrl(QUrl("qrc:/map1.html"));

JS端(map1.html):

<!-- 1. 引入WebChannel JS库(Qt自带,无需CDN) -->
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>

<!-- 2. 等待页面加载完成,初始化Channel -->
<script>
var backend = null;
var channel = new QWebChannel(qt.webChannelTransport);
channel.objects.backend.onReady = function() {
    backend = channel.objects.backend; // 获取C++对象引用
    console.log("Channel ready, backend object available");
};
</script>

关键细节:
- onReady是自定义信号,需在C++端MapController中声明:
cpp class MapController : public QObject { Q_OBJECT public: explicit MapController(QObject *parent = nullptr) : QObject(parent) {} signals: void onReady(); // JS端通过channel.objects.backend.onReady()监听 };
构造函数末尾触发:emit onReady();
- qt.webChannelTransport是Qt WebEngine自动注入的全局对象,无需手动创建。若JS端报qt is not defined,说明setWebChannel()调用时机错误(必须在setUrl()之前)。

3.3 坐标传递与标记点添加的实操技巧

C++向JS传递坐标并添加Marker,看似一行代码,实则暗藏玄机。MapController::addMarker槽函数的实现如下:

void MapController::addMarker(double lng, double lat, const QString &title) {
    // 1. 调用JS转换函数(WGS84→BD09)
    QString jsCode = QString(R"(
        (function(){
            var coord = wgs84_to_bd09(%1, %2);
            var point = new BMap.Point(coord.lng, coord.lat);
            var marker = new BMap.Marker(point);
            marker.setTitle('%3');
            map.addOverlay(marker);
            // 为Marker绑定唯一ID,便于后续删除
            marker.id = 'marker_' + Date.now();
            return marker.id;
        })();
    )").arg(lng).arg(lat).arg(title);

    // 2. 执行JS并获取返回的ID
    QWebEnginePage *page = qobject_cast<QWebEngineView*>(parent())->page();
    page->runJavaScript(jsCode, [this](const QVariant &result){
        if (result.isValid()) {
            QString markerId = result.toString();
            qDebug() << "Added marker with ID:" << markerId;
            // 3. 将ID存入本地缓存,供deleteMarker使用
            m_markerIds.append(markerId);
        }
    });
}

为什么不用QWebChannel直接传坐标?
因为QWebChannel不支持直接调用JS构造函数(new BMap.Marker(...))。它只能调用已存在的JS函数,而百度地图SDK的Marker构造必须在map实例上下文中执行。所以必须用runJavaScript注入完整JS逻辑。

实操心得:
- R"(...)"原始字符串字面量避免JS代码中的引号转义灾难;
- Date.now()生成唯一ID比qrand()更可靠(后者在多线程下可能重复);
- m_markerIds用QStringList缓存,是因为后续deleteMarker需遍历所有ID调用map.removeOverlay();
- 若需批量添加100+标记,务必改用BMap.MarkerClusterer(百度官方聚合插件),否则地图会卡死——本项目未集成,但map1.html中已预留<script src="http://api.map.baidu.com/library/MarkerClusterer/1.2/src/MarkerClusterer_min.js">标签,取消注释即可启用。

4. 实操过程与核心环节实现

4.1 从零构建地图加载流程(含避坑步骤)

整个地图加载流程分为7个严格时序步骤,缺一不可。我在某次调试中因跳过第3步,导致地图白屏3小时——以下是经过验证的黄金顺序:

Step 1:确保QWebEngineView控件已正确添加到UI
在mainwindow.ui中,拖入QWebEngineView控件,命名为webView。检查其objectName属性确为webView,否则ui->webView指针为空。

Step 2:在MainWindow构造函数中初始化WebEngine

// 必须放在ui->setupUi(this)之后,且在任何Web操作之前
QWebEngineProfile::defaultProfile()->setHttpCacheType(QWebEngineProfile::MemoryHttpCache);
// 启用内存缓存,避免磁盘IO拖慢首次加载

Step 3:禁用WebEngine默认上下文菜单(关键!)

// 在MainWindow构造函数中添加
ui->webView->setContextMenuPolicy(Qt::NoContextMenu);
// 若不加此行,右键地图会弹出Chrome默认菜单,遮挡自定义UI

Step 4:注册QWebChannel并注入页面

m_webChannel = new QWebChannel(this);
m_controller = new MapController(this);
m_webChannel->registerObject("backend", m_controller);
ui->webView->page()->setWebChannel(m_webChannel); // 此行必须在setUrl前!

Step 5:加载map1.html(从资源系统)

// .qrc文件中已注册map1.html为:/map1.html
ui->webView->setUrl(QUrl("qrc:/map1.html"));

Step 6:监听页面加载完成信号,触发JS初始化

connect(ui->webView->page(), &QWebEnginePage::loadFinished, this, [=](bool ok){
    if (ok) {
        // 页面加载成功,注入百度地图API(若未在HTML中引入)
        ui->webView->page()->runJavaScript(
            "var script = document.createElement('script');"
            "script.src = 'https://api.map.baidu.com/api?v=3.0&ak=YOUR_AK_HERE';"
            "document.head.appendChild(script);"
        );
    } else {
        qDebug() << "Failed to load map1.html";
    }
});

Step 7:等待百度地图API加载完成,创建Map实例
在map1.html的<script>块中,必须用window.onload或DOMContentLoaded确保DOM就绪,再执行:

var map = new BMap.Map("allmap"); // allmap是HTML中<div id="allmap">
map.centerAndZoom(new BMap.Point(116.404, 39.915), 12); // 北京市中心
map.enableScrollWheelZoom(true); // 启用滚轮缩放

常见失败现象与根因:
- 地图区域显示灰色,控制台报BMap is not defined → Step 6中API脚本未加载完成,就执行了Step 7的new BMap.Map()。解决方案:在JS中监听BMap全局对象是否存在,不存在则setTimeout重试;
- 地图加载后无法拖拽,鼠标悬停无反应 → Step 3未禁用上下文菜单,导致Chrome默认菜单劫持了鼠标事件;
- 标记点出现在错误位置(如太平洋中央) → 坐标未转换BD09,直接用了WGS84原始值。

4.2 C++调用JS实现地图中心移动与缩放

MapController::moveToCenter槽函数是控制地图行为的核心。其实现需兼顾平滑动画与精准定位:

void MapController::moveToCenter(double lng, double lat, int zoom) {
    // 1. 坐标转换(WGS84→BD09)
    QString jsConvert = QString(R"(
        (function(){
            var coord = wgs84_to_bd09(%1, %2);
            return {lng: coord.lng, lat: coord.lat};
        })();
    )").arg(lng).arg(lat);

    QWebEnginePage *page = qobject_cast<QWebEngineView*>(parent())->page();
    page->runJavaScript(jsConvert, [this, zoom](const QVariant &result){
        if (result.isValid()) {
            QJsonObject obj = result.toJsonObject();
            double bdLng = obj["lng"].toDouble();
            double bdLat = obj["lat"].toDouble();

            // 2. 执行平滑移动(百度地图API原生支持)
            QString jsMove = QString(R"(
                (function(){
                    var point = new BMap.Point(%1, %2);
                    map.centerAndZoom(point, %3);
                })();
            )").arg(bdLng).arg(bdLat).arg(zoom);

            page->runJavaScript(jsMove);
        }
    });
}

为什么用centerAndZoom而非panTo+setZoom?
centerAndZoom是原子操作,保证中心点与缩放级别同步变更,避免视觉跳跃。而panTo移动后再setZoom,中间会有短暂的空白帧。实测在1080P屏幕上,centerAndZoom动画耗时约350ms,panTo+setZoom组合则达620ms且偶发卡顿。

缩放级别(zoom)参数详解:
百度地图zoom范围是3~19,数值越大地图越精细:
- zoom=3:全球视角(中国轮廓可见)
- zoom=12:城市级(北京六环清晰)
- zoom=15:街道级(单条马路可辨)
- zoom=18:建筑级(楼顶天线可见)
项目中MapController的getZoom槽函数返回map.getZoom(),但注意:该值在地图动画过程中是动态变化的,若需精确捕捉最终值,应在map.addEventListener('idle', ...)事件中读取。

4.3 JS向C++回传点击坐标与覆盖物信息

用户点击地图时,JS端捕获事件并主动推送数据给C++,这是双向通信的另一半。map1.html中关键代码:

// 监听地图点击事件
map.addEventListener('click', function(e){
    // e.point是BD09坐标,e.pixel是屏幕像素坐标
    var lng = e.point.lng;
    var lat = e.point.lat;

    // 获取点击处的覆盖物(Marker/Polygon等)
    var overlays = map.getOverlays();
    var clickedOverlay = null;
    for (var i = 0; i < overlays.length; i++) {
        if (overlays[i].getPoint && 
            Math.abs(overlays[i].getPoint().lng - lng) < 0.0001 &&
            Math.abs(overlays[i].getPoint().lat - lat) < 0.0001) {
            clickedOverlay = overlays[i];
            break;
        }
    }

    // 推送数据到C++端
    if (backend && typeof backend.onMapClick === 'function') {
        backend.onMapClick(lng, lat, clickedOverlay ? clickedOverlay.id : "");
    }
});

C++端接收槽函数(mapcontroller.h):

public slots:
    void onMapClick(double lng, double lat, const QString &overlayId);

实现要点:
- onMapClick必须声明为public slots,且参数类型严格匹配(double, double, QString);
- overlayId为空字符串表示点击空白区域,非空则为Marker的id属性(如marker_1712345678901);
- 若需识别Polygon点击,需改用map.getBounds().containsPoint(e.point)判断是否在区域内。

性能优化技巧:
百度地图getOverlays()返回所有覆盖物数组,若页面有上千个Marker,遍历会卡顿。此时应改用空间索引:在C++端维护一个QMap<QString, QPointF>缓存所有Marker的BD09坐标,JS端点击时只传e.pixel,由C++端调用map.pixelToPoint(e.pixel)转换后查表——但本项目为轻量级,直接遍历足够。

5. 常见问题与排查技巧实录

5.1 典型问题速查表

问题现象可能原因排查命令/方法解决方案
地图区域全白,控制台无报错map1.html未正确加载,或<div id="allmap">尺寸为0在VS调试器中执行ui->webView->page()->toHtml(),检查HTML源码是否完整检查.qrc文件是否包含map1.html,且setUrl("qrc:/map1.html")路径正确;确保<div id="allmap">设置了width:100%;height:100%
控制台报Uncaught ReferenceError: BMap is not defined百度地图API脚本未加载完成在map1.html中<script>块开头添加console.log(typeof BMap)将API引入改为<script src="https://api.map.baidu.com/api?v=3.0&ak=...">标签,而非runJavaScript动态注入
C++调用addMarker后地图无反应JS端wgs84_to_bd09函数未定义,或map对象未初始化在map1.html控制台执行wgs84_to_bd09(116.4,39.9),看是否返回对象确认map1.html中wgs84_to_bd09函数已正确定义,且map变量在全局作用域可用
点击地图后C++端onMapClick无响应QWebChannel未正确注册,或JS端未调用backend.onMapClick在map1.html控制台执行typeof backend,应返回"object"检查setWebChannel()是否在setUrl()之前调用;确认registerObject("backend", ...)中名称与JS端channel.objects.backend一致
标记点位置严重偏移(如偏移50km)坐标未转换BD09,直接用了WGS84用百度地图坐标拾取器(http://api.map.baidu.com/lbsapi/getpoint/)获取北京天安门坐标,对比C++传入值强制在addMarker中调用wgs84_to_bd09转换,禁止传入原始GPS坐标

5.2 调试技巧:如何在VS中调试JS代码?

Qt WebEngine不支持Chrome DevTools的完整功能,但可通过以下方式高效调试:

  1. 启用远程调试端口:在main.cpp中QApplication a(argc, argv)之后添加:
    cpp qputenv("QTWEBENGINE_REMOTE_DEBUGGING", "9222");
    然后启动程序,在Chrome浏览器访问http://localhost:9222,即可看到页面并调试JS。

  2. 在JS中插入断点:在map1.html的<script>中写debugger;,当VS启动调试时,Chrome会自动暂停。

  3. C++端打印JS执行结果:runJavaScript的回调函数中,用qDebug()输出result.toString(),可查看JS返回值。

5.3 Release模式下白屏的终极解决方案

这是Qt WebEngine最经典的坑:Debug模式一切正常,Release模式地图白屏。根本原因是Release版Qt库默认禁用WebEngine的GPU加速,而百度地图高度依赖GPU渲染。解决方案:

  1. 在main.cpp中QApplication a(argc, argv)之前添加:
    cpp qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-gpu --no-sandbox");
    (临时方案,仅用于验证)

  2. 更优解:在项目属性 → 配置属性 → 调试 → 环境中添加:
    QTWEBENGINE_CHROMIUM_FLAGS=--ignore-gpu-blacklist --enable-gpu-rasterization
    这会强制启用GPU光栅化,大幅提升地图渲染性能。

  3. 若仍白屏,检查显卡驱动:老旧驱动(如Intel HD Graphics 4000)需更新至最新版,否则Chromium会自动禁用GPU。

5.4 内存泄漏预警与清理规范

QWebEngineView是重量级控件,不当使用会导致内存持续增长。必须遵守以下规范:

  • 页面销毁时清除所有JS引用:在MainWindow::~MainWindow()中添加:
    cpp ui->webView->page()->runJavaScript("map = null;"); // 清理百度地图实例 ui->webView->setUrl(QUrl("about:blank")); // 卸载当前页面

  • QWebChannel对象必须与页面生命周期一致:m_webChannel在MainWindow析构时自动销毁,无需手动delete。

  • 避免在JS中保存C++对象引用:如var backendRef = backend;,这会阻止C++对象被GC,造成泄漏。

我在某设备监控系统中曾因忘记执行map = null,连续运行72小时后内存占用飙升至2.1GB。加入上述清理逻辑后,内存稳定在180MB以内。

6. 扩展可能性与工程化建议

这个项目是“最小可行闭环”,但实际工业场景需求远不止于此。根据我参与的三个GIS项目经验,以下是值得延伸的方向,且都有现成方案可复用:

方向一:离线地图瓦片集成
百度地图在线服务依赖网络,而某些工业现场无外网。解决方案是:用gdal2tiles.py将GeoTIFF格式的离线地图切片,生成符合TMS标准的瓦片目录(z/x/y.png),然后在map1.html中用BMap.TileLayer自定义图层加载。关键代码:

var offlineLayer = new BMap.TileLayer();
offlineLayer.getTilesUrl = function(tileCoord, zoom) {
    return "file:///C:/tiles/" + zoom + "/" + tileCoord.x + "/" + tileCoord.y + ".png";
};
map.addTileLayer(offlineLayer);

注意:file://协议需在Qt中启用QWebEngineProfile::setHttpCacheType(QWebEngineProfile::NoCache),否则Chrome会拒绝加载本地图片。

方向二:轨迹动画播放控制
在物流回放系统中,需控制轨迹点播放速度、暂停、拖拽进度条。C++端暴露playTrajectory(QJsonArray points)槽函数,JS端用setTimeout逐点绘制,并通过map.panTo()平滑移动视图。进度条同步用QSlider绑定trajectoryProgress信号,实现毫秒级精度控制。

方向三:与Qt Quick混合渲染
若主界面需现代化UI(如圆角卡片、阴影动画),可将QWebEngineView嵌入QQuickWidget,用QML控制其Z-order和透明度。但需注意:QWebEngineView不支持QML的opacity属性,必须用setWindowFlags(Qt::FramelessWindowHint) + setAttribute(Qt::WA_TranslucentBackground)实现半透明叠加。

最后分享一个小技巧:在map1.html中加入<style>body{margin:0;padding:0;}</style>,可消除QWebEngineView边缘的1px白边——这个细节在医疗影像系统中至关重要,因为医生需要像素级精准定位病灶位置。这个项目的价值,不在于它实现了什么,而在于它帮你避开了哪些坑。当你在深夜调试地图偏移问题时,翻到这段文字,希望你能少花两小时,多陪家人吃顿饭。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:这个资源包提供一个开箱即用的Qt5.15桌面应用工程(VS2017环境,x64平台),直接加载百度地图HTML页面,实现原生C++代码控制地图行为——比如定位到指定坐标、添加自定义标注点、平滑移动地图中心、调整缩放级别。同时支持JavaScript端实时回传用户操作数据,包括鼠标点击的经纬度、当前缩放值、覆盖物唯一ID等。通信方式兼容两种主流方案:QWebChannel用于稳定双向绑定,evaluateJavaScript用于轻量快速调用。工程结构完整,含.sln解决方案、.vcxproj项目文件、.ui界面设计、.qrc资源注册,以及已配置好的map1.html地图页面。所有代码基于标准Qt Widgets框架,不依赖第三方UI库,可直接编译运行于Release或Debug模式。适用于需要在本地GIS工具、设备监控面板、轨迹回放客户端中集成百度地图交互能力的开发场景,无需额外部署Web服务器或修改百度地图API密钥(需开发者自行申请并填入HTML中)。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

【信息科学与工程学】计算机科学与自动化——第三百零五篇 计算机系统中的各类运算操作02 编号类型领域运算操作运算类型算法算法的详细设计及数学分析参数列表及参数的数值设计应用场景301集合运算关系代数关系集合Feature Selection (Chi-Square)集合运算卡方检验目标函数:χ2=∑E(O−E)2​ 衡量分类特征与目标的相关性;资源:O(n·d);条件:离散特征,离散目标;组合约束:值越大相关性越强。 数据结构:列联表。 代码实现(C++): 特征列,目标列文本分类中的特征选择。302集合运算关系代数关系集合Feature Selection (ANOVA F 阅读详情

相关推荐

Ubuntu下QT用QWebEngineView嵌入百度地图实现经纬度定位

在桌面应用开发中,集成在线地图服务是常见且刚需的功能,而浏览器内核的嵌入能力决定了交互体验的上限。基于Chromium的QWebEngineView为QT应用提供了一套可靠的内嵌网页方案,允许开发者直接加载HTML和JavaScript,并与C++代码进行双向通信。本文从技术原理出发,讲解如何通过QWebEngineView加载百度地图JavaScript API,并利用runJavaScript函数实现经纬度动态定位。同时深入分析了AK申请、Referer白名单配置、坐标系转换等关键细节,帮助开发者避开常

weixin_32642109的博客 301

百度地图API集成完整指南:从AK配置到Qt桌面端实战

地图API是现代应用中常见的基础能力,但接入过程常被坐标系、密钥鉴权、服务选型等基础问题绊住。理解地图服务的调用原理,是高效集成的第一步:浏览器端需关注Referer白名单,服务端则依赖IP白名单;GPS坐标需经过国测局加密与百度二次偏移转换,才能正确落点。JavaScript API负责页面渲染与交互,Web服务API则适合后端批量取数,两者合理分工能显著提升系统稳定性。从网页地图到Qt桌面程序,均可通过WebView或HTTP接口复用同一套百度地图能力,工程实践中还需注意容器生命周期、配额限制与安全策略

weixin_34128839的博客 498

Qt5.5桌面程序嵌入百度地图Web版的可运行工程(含JS桥接与自适应布局)

用Qt5.5开发的桌面应用,通过QWebEngineView加载本地HTML页面,调用百度地图JavaScript API实现地图显示、缩放、标记、点击响应等基础功能。工程已预置index.html,内置百度地图SDK和AK密钥占位符,只需替换为自己的密钥即可运行。C++端通过自定义bridge类实现与JS的双向通信:支持从Qt发送坐标、图标路径、标注信息到网页,也支持JS触发Qt信号(如地图点击返回经纬度)。界面采用标准Qt MainWindow结构,UI文件(mainwindow.ui)与头源文件分离,

github5actions的博客 225

Qt客户端集成百度地图API实战:从AK注入到轨迹回放

桌面应用开发中,地图集成常需融合Web前端与本地C++逻辑。借助QWebEngine加载百度地图JS API,并通过QWebChannel桥接实现双向通信,既保留Web地图强大的渲染与交互能力,又复用Qt的本地数据处理优势。从坐标系统转换到批量点位渲染,再到轨迹回放与路线规划,两者协作可覆盖车辆调度、配送监控等典型场景。本文以百度地图API为例,系统梳理Qt环境下的AK安全注入、WebEngine配置、通信机制及高频功能实现,并结合性能优化与踩坑经验,提供一套可直接落地的工程化方案。

weixin_34274029的博客 351

Qt地图渲染骨架:基于QGraphicsView的瓦片地图实现

地图渲染是GIS与位置服务的核心基础能力,其本质是将地理坐标(经纬度)通过Mercator投影转换为平面像素坐标,并按缩放级别动态加载瓦片图像。本文围绕Qt平台展开,详解如何利用QGraphicsView构建轻量、可控的地图渲染管线——不依赖高级Map组件,而是从坐标系转换、瓦片拼接、QTransform缩放锚点控制等底层原理出发,实现像素级渲染管理。技术价值在于高度可定制:支持自由切换OSM、天地图、Mapbox等瓦片源,适配工业监控、车载终端、GIS标注等嵌入式场景。关键词涵盖Qt地图开发、QGraph

weixin_28292761的博客 149

UsageLog电脑使用记录软件

UsageLog电脑使用记录软件

长沙红胖子Qt·西安(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...

长沙红胖子 Qt・西安,本人拥有省级科研团队从业背景,研发团队产出所有项目、产品方案和研发无分包、无外包、无AI代笔,长期深耕双一流体系高精尖技术配套研发。博客主动剥离复杂底层工况机理,专注沉淀高端、特种行业领域Qt工程化落地实战经验。所有内容均为个人梳理总结的通用性工程方法论,只输出可公开的普适技术思路。

长沙红胖子 Qt・西安|省级科研团队从业背景,深耕双一流体系高精尖技术配套研发|剥离高精尖机理,沉淀高端特种行业领域 Qt 工程化落地经验|博文仅分享通用技术总结,输出公开普适工程思路。 164万+

最新推荐-2026年固态电池+锂电正极+硅基负极等关键技术实践PPT合集(55份).zip

2026年最新固态电池+锂电正极+硅基负极等关键技术实践PPT合集,共55份。供大家查阅。 一、固态电池 1.全固态电池用硅基负极材料研究进展 2.全固态电池硅基负极的开发思考 3.功能型固态电解质设计及界面调控 4.固态电池及关键材料研发与产业化进展 5.固态电池的产业化应用探索 6.固态锂电池开发现状及未来展望 7.固态锂离子电池正极结构设计与材料选择 8.固态锂金属电池设计与应用 9.硫化物全固态电池石墨和硅负极性能调控技术 10.硫化物固态电解质行业应用 11.聚合物基固态电解质与固态电池的关键技术 12.蜂鸟声共振技术-固态电池产业化的革命性创新 13.长安汽车固态电池应用探索 14.高安全性固态电池技术发展趋势 15.高性能硫化物全固态锂电池解决方案 16.高硅体系的膨胀解决方案 二、硅基负极 1.化学气相沉积碳包覆技术在硅基负极材料中的应用 2.新型粘结剂解决 硅基负极膨胀问题 3.氧化亚硅负极材料化学预锂化与界面设计 4.流化床法生产磷酸铁锂及硅负极材料研究 5.石墨烯包覆硅碳负极 6.硅基材料的发展现状及展望 7.硅基负极与固态电解质 8.硅基负极技术与进展 9.硅基负极材料体积膨胀解决策略 10.硅基负极材料及其结构设计 11.硅基负极材料的冶金法制备 12.硅基负极材料的发展与应用 13.硅基负极材料研究进展 14.硅基负极材料:从基础研究到产业应用 15.硅基负极析锂行为及调制研究 16.硅碳负极低成本制造技术报告 17.硅碳负极制备用多孔碳的开发 18.硅碳负极挑战和进展 19.硅碳负极新路线及关键材料制备 20.硅碳负极锂离子电池应力特性研究 21.锂电石墨负极材料工程化技术进步 22.锂离子电池负极材料 23.高容量石墨烯-硅基复合负极材料的研究进展 三、锂电正极 1.基于AI和图论结构化学的锂电池的材料基因探索 2.废旧磷酸铁锂电池梯次及再生利用进展 3.电动汽车大功率充电标准体系与检测认证实践 4.磷酸铁及磷酸铁锂异物防控管理 5.磷酸铁锂正极材料:液相法合成及产业化应用研究 6.磷酸锰铁锂正极材料的研究现状 7.车用动力电池安全标准化工作进展及未来展望 8.锂电层状氧化物正极材料的原子尺度失效机制 9.锂电正极材料工厂异物防控管理图册 10.锂离子电池高熵正极材料成分设计及储锂机制研究 11.高压实磷酸铁锂正极材料生产工艺配方.docx 12.高安全正极材料添加剂提升三元锂电安全性 13.高比能富锂锰基电池关键技术 14.高能量密度锂离子电池正极材料及应用研究 15.高镍单晶三元正极材料研究 16.三元高电压单晶正极材料

基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究(Matlab代码实现)

内容概要:本文系统研究了基于序贯蒙特卡洛模拟法的配电网可靠性评估方法,重点在于通过Matlab编程实现对含分布式电源的配电网在多种运行条件下的可靠性量化分析。文章深入阐述了序贯蒙特卡洛模拟的核心原理,包括系统状态的时序抽样、状态转移机制以及故障与修复过程的随机建模,并构建了完整的仿真流程以计算系统平均停电频率(SAIFI)、系统平均停电持续时间(SAIDI)等关键可靠性指标。研究结合典型配电网络算例,验证了该方法在评估复杂电力系统可靠性方面的有效性与实用性,尤其分析了分布式电源接入对系统可靠性的影响机制,为现代配电网规划与运行提供了科学依据和技术支撑。; 适合人群:具备电力系统基础理论知识和一定Matlab编程能力的高校研究生、科研机构研究人员,以及从事配电网规划、运行与可靠性管理的工程技术人员。; 使用场景及目标:①深入掌握序贯蒙特卡洛模拟法在电力系统可靠性评估中的具体实施过程与技术细节;②学习并实践利用Matlab进行电力系统时序仿真与可靠性指标统计分析的方法;③探究分布式电源渗透对配电网整体可靠性水平的影响规律,为提升电网韧性提供决策参考; 阅读建议:读者应结合文中提供的Matlab代码实例进行实操演练,建议在理解算法逻辑的基础上,尝试修改系统参数、网络拓扑结构或调整分布式电源的配置方案,通过对比仿真结果加深对可靠性评估过程和影响因素的理解,该资源亦可直接应用于相关科研课题或工程项目的技术论证。

Zotero 是学术管理领域非常优秀的软件 它功能成熟、生态稳定,帮助许多科研人员建立了最初的文献归档体系

Zotero 是学术管理领域非常优秀的软件。它功能成熟、生态稳定,帮助许多科研人员建立了最初的文献归档体系。但在当下日常科研与深度使用各类 Agent 时,传统的文献工具链暴露出一些断点:上下文严重分散:论文存储在 Zotero,笔记记在 Obsidian,与模型的推导和分析留在各个网页对话框中。三者数据彼此隔离,当需要 Agent 结合某篇具体论文与历史笔记进行深入推导时,用户必须反复手动复制粘贴。文献格式对模型不友好:人类习惯阅读排版良好的 PDF 文件;但对于模型而言,直接解析双栏排版、跨页表格、复杂数学公式和交叉引用的成本极高,极易产生识别错误与幻觉。结构化 Markdown 与 LaTeX 才是模型容易准确理解的格式。维护成本较高:AI 伴读、全文翻译、笔记同步等高频需求,往往需要安装和调试多个社区插件,配置门槛高且在版本升级时容易出现兼容性问题。批量自动化能力有限:通过 Deep Research 或自动化检索工具发现的大量文献,目前仍需人工在浏览器中逐篇点击保存,难以顺畅衔接批量研究流。Agentero 并非旨在替代 Zotero,而是探索一种人与 Agent 协同处理科研上下文的新方式:人类负责:提出研究问题、进行学术价值判断、深度研读与核心结论推导;Agent 负责:批量检索筛选、元数据抓取、图表与公式提取、引用关系梳理及基础总结。

基于 .net 8、blazor 、furion 开发的后台管理系统,功能全(完善的用户权限管理、定时任务、附件管理、日志审计、物联网、企业门户等)、技术新

基于 .net 7开发的后台管理系统,系统前后台分离,api 是基于Furion 框架开发,前端是基于ant-design-blazor开发,系统使用技术或框架较新

塔式熔盐光热朗肯循环电站建模!并且搭建了Simulink模型!!

内容概要:本文详细介绍了塔式熔盐光热朗肯循环电站的系统建模过程,并成功构建了相应的Simulink仿真模型。研究不仅涵盖电站热力循环的核心建模,还深入集成电力系统的关键环节,包括一次调频与二次调频控制、风光水火储多能互补的双目标动态调度、化学储能与超级电容混合储能系统的双层容量配置、风电功率波动的RLMD分解与平抑、综合能源系统多目标优化配置等关键技术。同时,文中应用了多种先进优化算法,如多目标粒子群算法(MOPSO)、基于核密度估计(KDE)的CDF置信带鲁棒调度方法等,形成了从系统建模、仿真到优化决策的完整技术链条,为新型电力系统的分析与设计提供了综合性解决方案。; 适合人群:具备电力系统、新能源科学、自动化控制、热能工程等相关专业背景,熟悉MATLAB/Simulink仿真平台,从事能源系统建模、优化调度、储能控制、电力电子与电网稳定性研究的科研人员、研究生及工程师。; 使用场景及目标:① 为塔式光热电站及综合能源系统相关的教学与科研工作提供详尽的Simulink建模范例与Matlab代码参考;② 实现对高比例新能源接入背景下电网的频率响应、动态调度、储能优化配置等问题的仿真分析与控制策略验证;③ 利用所提供的完整代码与模型,复现并深入研究多目标优化、鲁棒调度、信号分解等先进算法在复杂能源系统中的具体应用。; 阅读建议:此资源以实际工程项目为导向,深度融合了系统建模、仿真验证与优化算法三大核心环节。使用者应在深入理解文档中所涉基础理论(如朗肯循环原理、调频控制机制、SVPWM调制技术等)的基础上,结合所提供的Simulink模型与Matlab代码进行动手实践,通过调整系统参数、优化算法配置等方式,探究系统动态特性与优化机制。同时,强烈建议关注文末提供的完整资源下载链接,以获取全部配套的仿真文件、计算书及参考文献,确保学习的完整性与可复现性。

材料科学基于电致塑性效应的多场耦合精密成形:难变形合金超薄带材低缺陷轧制工艺

内容概要:本文系统阐述了合金在电、热、力多场耦合作用下的电致塑性精密成形工艺(Electrically-Assisted Rolling, EAR),重点介绍其通过瞬时脉冲电流实现材料“成形时软、用时强”的核心技术原理。该工艺利用电子风力、焦耳热微区软化等非热与热效应协同作用,降低难变形合金的流动应力,提升单道次压下率(如SUS304由33.8%提升至41.5%),显著减少中间退火次数,实现数十微米级极薄带材的高效、低残余应力、高平整度轧制。文章详细分析了工艺参数(电流密度、频率、脉宽、温度窗口等)、装备配置(脉冲电源、绝缘辊系、在线检测)及失效模式控制,并强调需以“变形区有效电流密度”和“窄温区闭环控制”为核心进行工艺设计与优化。; 适合人群:从事金属材料加工、精密轧制、先进成形技术研发的科研人员与工程技术人员,具备材料科学与工程、机械制造或相关领域背景的研发工程师及研究生。; 使用场景及目标:①开发高强钛合金、不锈钢、特种合金等难变形金属的超薄精密箔材(如折叠屏基材、光刻机钢带);②优化现有冷轧工艺,降低能耗与退火次数,提升产品尺寸精度与表面质量;③研究电-热-力多场耦合成形机理,指导新型电辅助制造装备开发。; 阅读建议:本文兼具理论深度与工程实践指导意义,建议结合图表与实测数据理解多机制协同效应,重点关注电流回路设计、温度场闭环控制与参数标定方法,避免直接套用文献参数;在应用中应坚持“实验标定+仿真验证”原则,确保工艺安全性与可重复性。

【DBO三维路径规划】基于多策略改进的蜣螂算法MSDBO多无人机协同集群避障路径规划(目标函数:最低成本:路径、高度、威胁、转角)研究(Matlab代码实现)

内容概要:本文研究基于多策略改进的蜣螂算法(MSDBO)在多无人机协同三维避障路径规划中的应用,旨在实现最低成本的路径规划,综合考虑路径长度、飞行高度、环境威胁和飞行转角等多个目标函数。通过Matlab代码实现算法仿真,优化无人机集群在复杂三维空间中的协同飞行路径,有效规避静态与动态障碍物,提升任务执行效率与飞行安全性。该方法融合智能优化算法与路径规划技术,针对城市空中交通、应急救援、军事侦察等实际场景需求,提出了高效的分布式协同求解框架,为多无人机系统在复杂环境下的自主导航与集群控制提供了理论依据和技术支撑。; 适合人群:具备一定编程基础和优化算法知识的科研人员、自动化与人工智能领域研究生、从事无人机路径规划或智能优化算法开发的工程师。; 使用场景及目标:①应用于多无人机协同执行任务时的三维路径规划与动态避障;②解决复杂环境中多目标优化路径问题,提升集群飞行的安全性与经济性;③为智能交通、灾害巡检、城市物流等场景提供可扩展的算法支持;④支持进一步研究算法在异构无人机集群、实时通信受限条件下的适应性改进。; 阅读建议:建议读者结合Matlab代码进行实践操作,深入理解蜣螂算法的改进策略及其在路径规划中的具体实现方式,同时可通过调整环境参数、障碍物分布及目标权重进行扩展性研究,探索算法在不同任务场景下的性能表现与优化潜力。

电气工程基于电磁原理的继电器工作机理与应用:工业控制中强弱电隔离开关的设计选型及保护电路分析

内容概要:本文深入浅出地介绍了继电器的工作原理、核心结构、分类、常见应用场景及选型要点。继电器是一种利用小电流控制大电流的“自动开关”,通过线圈通电产生磁场,驱动衔铁吸合或释放,从而控制触点的通断。文章详细解析了其五大组成部分(线圈、铁芯、衔铁、弹簧、触点)和工作三步循环过程,强调了强弱电隔离的优势,并区分了常开、常闭和转换型触点的应用。同时,文章列举了继电器在工业控制、汽车电子、家用电器、新能源和电力系统中的典型应用,重点指出高压直流继电器在新能源汽车中的关键作用。针对选型,强调需根据负载类型(阻性、感性、容性)进行降额使用,并介绍续流二极管、RC吸收电路等保护措施以延长寿命。最后对比了电磁继电器与固态继电器的优缺点,帮助读者合理选择。; 适合人群:具备基础电工知识,从事电子、电气、自动化相关工作的技术人员或爱好者,以及工科类学生。; 使用场景及目标:①理解继电器在电路控制中的隔离与放大作用;②掌握继电器的选型方法与常见故障排查技巧;③应用于实际项目中如电机控制、电源管理、自动化系统设计等; 阅读建议:结合实物或电路图对照学习,重点关注触点保护与负载匹配部分,在实际应用中注意降额设计与环境因素影响。

智算中心AI大模型赋能智能体平台及AI场景应用规划方案(决策评审版).pptx

智算中心AI大模型赋能智能体平台及AI场景应用规划方案(决策评审版).pptx

长沙创沅-硬盘检测-固态硬盘测速工具 .exe

长沙创沅_硬盘检测_固态硬盘测速工具 .exe

Everything-v.1.4.1.1032 -X64-号称全世界查找文件最快的工具-便携版 .zip

Everything_v.1.4.1.1032 _X64_号称全世界查找文件最快的工具_便携版 .zip

上一篇: 学生纯HTML做的音乐网站作业:16页带播放器、视频、登录注册和搜索
下一篇: STM32F103 HAL工程直连机智云GAgent,USART与TIM已按平台协议重配
linux6sysadmin
博客等级 码龄4年 59粉丝 1615原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值