十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

Ubuntu下QT用QWebEngineView嵌入百度地图实现经纬度定位 简介面向需要在 Ubuntu 操作系统中集成百度地图的 Qt 开发者这份小体积示例包演示了如何借助 Qt 的网页视图组件加载本地网页文件并实现输入经纬度后显示对应位置。资源总共包含 9 个文件其中有 2 个网页页面、2 个 C 源文件、1 个头文件、1 个界面设计文件、1 个工程管理文件以及若干构建辅助文件压缩包整体仅 9KB结构清晰明了非常适合快速移植到已有项目中。关键实现思路可以从代码中直接读到先安装 Qt 基础开发包与网页引擎开发包在页面中引入百度地图脚本库布置经纬度输入框和定位按钮点击后利用地图坐标对象设置中心点并添加标记Qt 端则加载该页面并通过监听页面地址变化来调用页面中的定位函数完成桌面端与网页端的交互。如今已有 1231 人学习下载适合希望在 Qt 应用里快速加入地图能力的开发者。拿到材料后可以直接运行调试并能够在此基础上继续扩展自动补全、多点标记、地址解析等实用功能省去从零研究地图接口和混合编程调试的精力。 Ubuntu 下用 QT 写桌面程序想直接调用百度地图做界面展示很多人第一反应是去翻官方 SDK结果查一圈发现百度地图客户端 SDK 只有 Android、iOS、小程序这些端桌面端根本没给 QT 留接口。好在 QT 从 5.6 开始集成了基于 Chromium 内核的 QWebEngineView完全可以把百度地图的 HTML 网页当作一个控件嵌进窗口里。这篇文章把整个过程完整跑一遍申请百度地图 AK、编写可运行的地图 HTML、在 QT 工程里加载页面并输入经纬度定位代码可以直接抄去改。不管你是做 Ubuntu 桌面开发项目还是在工控机上用 QT 套一个地图显示模块只要需求定位是“嵌入式网页地图”这套方案都能用换成高德地图、天地图、Leaflet 也是同一个思路。1. 方案选型为什么用 QWebEngineView 嵌入百度地图1.1 需求拆分与技术栈这个需求拆开其实就三件事一个能渲染网页的组件一份包含百度地图 JS API 的 HTML以及 C 向网页传递参数的方法。对应到 QT 侧就是 QWebEngineView QLineEdit QPushButton 的组合。QWebEngineView 负责加载和渲染地图页面QLineEdit 接收用户输入的经纬度QPushButton 触发定位。定位动作的核心只是一行page()-runJavaScript()把坐标拼成 JS 函数调用丢给网页执行。整个架构就是“浏览器壳 Web 页面”的模式QT 只做宿主外壳和业务控制地图渲染、缩放、标注全部交给百度地图 API。好处很明显地图能力由官方维护后续想加 POI 搜索、路线规划只需要改 HTMLC 侧几乎不动。1.2 备选方案对比动手之前我也对比过其他路子方案维护成本地图功能适用场景QWebEngineView 地图 JS API低地图能力由官网更新搜索、路线、图层完整桌面端嵌入式地图QWebView QtWebKit极高Qt 5.5 后已移除受旧内核兼容性限制遗留项目维护自绘瓦片 QPainter高切片拼接逻辑自研只有最基础的地图显示纯离线场景启动外部浏览器最低完整不需要嵌入界面时排除法走下来QWebEngineView 是唯一“既能嵌入桌面界面、又不用自己造轮子”的方案这也是目前社区里做 QT 桌面地图的主流做法。1.3 C 与网页之间的三种通信方式C 向网页传参数常见的有三条路runJavaScript单向调用C 直接把 JS 代码丢给页面执行。适合“输入坐标 - 定位”这种场景。QWebChannel双向通道网页里的 JS 也能回调 C 对象。适合“点击地图 - 坐标回传 C”这种反向交互。URL 参数拼接加载时把坐标拼到 URL 问号后面。只适合初始化定位后期交互很别扭。本项目最核心的场景是单向的输入定位用 runJavaScript 就足够了。把这条主流程走通以后反向交互再扩展也不迟。2. Ubuntu 环境准备与百度地图 AK 申请2.1 QWebEngine 模块安装与验证用官方在线安装器装 QT 的朋友最容易踩坑基础组件选完之后WebEngine 是单独的模块必须手动勾选 Qt WebEngine否则编译的时候#include QWebEngineView直接报找不到头文件。走 apt 安装路线在 Ubuntu 20.04/22.04 上执行sudo apt update sudo apt install qtbase5-dev qtwebengine5-dev装完想验证模块是否存在直接建一个空 QWebEngineView 工程编译一次最可靠。如果系统里同时装了 apt 版和官网版 QT环境变量容易打架出现莫名其妙的链接错误时先执行which qmake看当前用的到底是哪一套。2.2 AK 申请与 Referer 白名单配置百度地图开放平台注册登录后在“应用管理 - 创建应用”里填应用名称应用类型选“浏览器端”。创建页有一个极其关键的选项叫 Referer 白名单本地调试阶段直接填*放通所有来源。很多人在这一步填了 localhost 或者域名结果程序一加载地图就报 200 错误Referer 不合法。原因在于 qrc:// 或 file:// 协议加载本地 HTML 时Chromium 带过去的 Referer 要么为空要么是qrc:///resources/map.html这种非标准 scheme百度后端匹配不上你填的域名。注意无论 HTML 放在本地文件还是 qrc 里本地调试阶段 AK 的 Referer 白名单都填*等上线部署到服务器再改成真实域名。拿到 AK 后替换进 HTML 里的 script src 即可script typetext/javascript srchttps://api.map.baidu.com/api?v3.0ak你的AK/script3. 核心实现地图 HTML 与 QT 工程的完整对接3.1 地图 HTML 编写要点map.html 的完整代码放下面核心是定义一个全局的 locate 函数外部才能用window.locate()调到!DOCTYPE html html langzh-CN head meta charsetutf-8 title百度地图定位页/title style html, body { margin: 0; padding: 0; width: 100%; height: 100%; } #map-container { width: 100%; height: 100%; } /style script typetext/javascript srchttps://api.map.baidu.com/api?v3.0ak你的AK/script /head body div idmap-container/div script var map null; function initMap() { map new BMap.Map(map-container); map.centerAndZoom(new BMap.Point(116.403873, 39.914889), 15); map.enableScrollWheelZoom(true); map.addControl(new BMap.NavigationControl()); map.addControl(new BMap.ScaleControl()); } function doLocate(lng, lat) { var point new BMap.Point(lng, lat); map.centerAndZoom(point, 17); map.clearOverlays(); var marker new BMap.Marker(point); map.addOverlay(marker); var infoWindow new BMap.InfoWindow( 经度: lng br纬度: lat, { width: 220, title: 定位坐标 } ); marker.addEventListener(click, function () { map.openInfoWindow(infoWindow, point); }); } function locate(lng, lat) { if (!map) { console.error(地图尚未初始化); return; } doLocate(parseFloat(lng), parseFloat(lat)); } window.onload initMap; /script /body /html几个细节必须提醒定位函数必须写在全局作用域里模块方式导出的函数 C 侧不一定拿得到。BMap.Point 构造函数接收的是数字lng、lat 进来后做一次 parseFloat 兜底避免传字符串在部分版本里出问题。每次定位前先 clearOverlays()否则上一个标记还留在图上视觉上很乱。InfoWindow 里的内容用字符串拼接就能显示坐标不用搞复杂模板。3.2 QT 工程代码与资源文件新建一个 Widgets 工程pro 文件长这样QT core gui webenginewidgets greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET map_demo TEMPLATE app SOURCES main.cpp RESOURCES resources.qrc用 qrc 资源把 HTML 打进二进制运行时永远能找到页面不受工作目录影响。qrc 文件RCC qresource prefix/ fileresources/map.html/file /qresource /RCCmain.cpp 的完整代码#include QApplication #include QWebEngineView #include QLineEdit #include QPushButton #include QVBoxLayout #include QHBoxLayout #include QLabel #include QWidget #include QUrl #include QDebug #include cmath int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget window; window.setWindowTitle(QT 调用百度地图 - 经纬度定位); window.resize(1000, 700); QWebEngineView *view new QWebEngineView(window); view-setUrl(QUrl(qrc:/resources/map.html)); view-setContextMenuPolicy(Qt::NoContextMenu); QLabel *lngLabel new QLabel(经度:, window); QLineEdit *lngEdit new QLineEdit(116.403873, window); QLabel *latLabel new QLabel(纬度:, window); QLineEdit *latEdit new QLineEdit(39.914889, window); QPushButton *locateButton new QPushButton(定位, window); locateButton-setEnabled(false); QHBoxLayout *toolLayout new QHBoxLayout; toolLayout-addWidget(lngLabel); toolLayout-addWidget(lngEdit); toolLayout-addWidget(latLabel); toolLayout-addWidget(latEdit); toolLayout-addWidget(locateButton); toolLayout-addStretch(); QVBoxLayout *mainLayout new QVBoxLayout(window); mainLayout-addWidget(view, 1); mainLayout-addLayout(toolLayout); QObject::connect(view, QWebEngineView::loadFinished, [](bool ok) { if (ok) { locateButton-setEnabled(true); } }); QObject::connect(locateButton, QPushButton::clicked, []() { bool okLng false; bool okLat false; double lng lngEdit-text().trimmed().toDouble(okLng); double lat latEdit-text().trimmed().toDouble(okLat); if (!okLng || !okLat || std::abs(lng) 180 || std::abs(lat) 90) { qDebug() 经纬度不合法; return; } QString js QString(window.locate(%1, %2);).arg(lng).arg(lat); view-page()-runJavaScript(js); }); window.show(); return app.exec(); }代码里已经处理了几个隐性坑定位按钮初始禁用等 loadFinished 信号确认页面加载完成再启用经纬度做了范围和格式校验runJavaScript 拼串时用%1、%2直接填充 double不会出现千分位分隔符之类的问题。3.3 编译运行与效果验证mkdir build cd build qmake ../map_demo.pro make ./map_demo正常启动后界面上面是地图下边一条经度、纬度输入框和定位按钮。改输入框里的数值点击定位地图会跳到对应位置并落下标记点点击标记出现带坐标的弹窗。到这里核心需求就算跑通了下面两个优化点强烈建议一起做。4. 定位交互优化与坐标偏移处理4.1 通过 loadFinished 控制定位按钮前面 main.cpp 里已经用了 loadFinished 控制按钮页面没加载完就置灰加载完成才允许点击。这种做法可以避免用户一启动就点定位而此时 HTML 里的 locate 函数还不存在runJavaScript 调用会静默失败控制台也没有明显报错。另外还有一个细节百度地图 JS API 是外部脚本网络慢的时候 loadFinished 会相应延迟。所以在 HTML 里保留if (!map)判空双保险。程序里能容忍的最长等待时间按实际网络情况评估内网和公网差别很大没有统一标准。4.2 坐标系不一致导致定位偏移这是最容易忽略的“伪 Bug”。如果你手里的经纬度来自 GPS 设备或手机定位导出的数据大概率是 WGS84 坐标系而百度地图内部用的是 BD09两者在城市尺度上能差出几百米定位点肉眼可见地偏。解决办法是进百度地图前做坐标转换。前端最省事的是用 BMap.Convertorfunction locateWithConvert(lng, lat) { var convertor new BMap.Convertor(); var points [new BMap.Point(lng, lat)]; convertor.translate(points, 1, function (data) { if (data.status 0) { var p data.points[0]; doLocate(p.lng, p.lat); } }); }translate 的第二个参数 1 表示原始坐标是 WGS84回调里拿到的就是 BD09 坐标。如果确定输入本来就是百度坐标直接走 locate 反而更快。C 侧也能处理转换但既然前端有能力少一层网络请求和 C 代码总是好的。4.3 点击地图回传坐标QWebChannel 扩展需求一旦变成“在地图上点一下把坐标填回输入框”单向的 runJavaScript 就不够用了。这时候启动 QWebChannelC 侧注册一个 QObject 到 channelHTML 里通过 qt.webChannelTransport 拿到这个对象在地图 click 事件里调用 C 槽函数。代码结构不复杂但建议把 C 侧的回调对象单独抽成一个 Bridge 类槽函数写在里面避免 main.cpp 越来越臃肿。这个扩展方向后期可以做一篇独立文章核心思路就是“反向通信”。5. 白屏、JS 无响应等典型问题排查5.1 问题速查表现象可能原因解决办法程序启动后地图区域一片白Chromium 沙箱或显卡驱动问题设置QTWEBENGINE_CHROMIUM_FLAGS--no-sandbox必要时加--disable-gpu页面加载但地图不显示AK 无效或 Referer 不匹配核对 AK白名单填*再试点击定位没反应HTML 未加载完locate 不存在loadFinished 信号控制按钮状态标记与实际地点偏差几百米坐标系不一致用 BMap.Convertor 或 geoconv 接口转换qrc 修改后页面不更新编译缓存删除 build 目录重新 qmakerunJavaScript 报解析错误经纬度里混入非法字符toDouble 校验后再拼接 JS5.2 远程调试网页与 root 环境处理QWebEngineView 内置了 Chromium可以开远程调试端口看网页 console。运行前设置环境变量QTWEBENGINE_REMOTE_DEBUGGING9222启动后用 Chrome 打开localhost:9222就能像调试普通网页一样看 console、检查元素、手动执行 JS。定位函数失效时到底是 BMap is not defined 还是 locate is not defined一眼就能分清。再说一个 Ubuntu 下非常常见的坑在虚拟机或服务器上用 root 用户跑程序Chromium 默认禁止沙箱窗口直接白屏。解决办法是启动前加上export QTWEBENGINE_CHROMIUM_FLAGS--no-sandbox --disable-gpu ./map_demo虚拟机没装显卡驱动时还可以再配合export LIBGL_ALWAYS_SOFTWARE1强制 Mesa 软件渲染白屏问题基本都能解决。6. 扩展玩法与经验心得6.1 打包发布注意事项发布时 WebEngine 依赖的资源文件qtwebengine_resources.pak、icudtl.dat 等必须和可执行文件放一起推荐用 linuxdeployqt 自动收集。百度地图 JS API 依赖外网离线环境跑不起来除非换离线瓦片方案或者对接公司内部的瓦片服务这一点要在需求阶段就跟产品确认好。另一个小事项如果 HTML 用 qrc 方式打包进了二进制AK 的 Referer 白名单建议在生产环境配成实际访问域名而不是继续用*。开发期无所谓上线后还是要收紧安全边界。6.2 我踩过坑后的几条心得先把 HTML 独立在浏览器里调通再塞进 QT。我见过太多人把两步揉在一起出了 bug 分不清是 C 的问题还是网页的问题。先在 Chrome 里打开 map.html能看到地图、能手动调 locate确认无误后再进工程排查范围一下子就缩小了。AK 不要硬编码提交到公共仓库泄露出去被别人盗用账号额度会被刷爆轻则限流重则封禁。正式系统里用配置文件或者环境变量注入这个习惯要趁早养。定位 marker 建议用一个全局变量保存引用后面更新 InfoWindow 或者做轨迹绘制时有据可查。地图默认控件自带缩放按钮但滚动缩放在桌面端体验更好HTML 里已经开了 enableScrollWheelZoom这个开关在初版代码里经常会漏漏掉之后用户会以为地图卡死了排查起来又容易忽略写的时候要当个必选项记着。本文还有配套的精品资源点击获取
返回列表