Qt加载百度地图详细流程(附带报错解决方法)
作者:求一个demo
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
内容通俗易懂,废话不多说,我们直接开始------>>>>>>
!!!!!!成果展示!!!!!!
一、创建一个Qt工程(QWidget/QMainWindow均可)
我的Qt环境:Qt是5.14.2,Qt Creator是4.11.1。
(此处以创建QMainWindow为例)
二、在百度地图开发者平台获取密钥
1、首先在“百度地图开放平台”注册一个账号,链接:百度地图-百万开发者首选的地图服务商,提供专属的行业解决方案
2、选择控制台。
3、点击“应用管理”—“我的应用”—“创建应用”。
4、应用类型一定要选择“浏览器端”,Referer白名单的话,如果自己没有专门的需求,填一个星号*就行,然后保存自己的AK。
三、创建网页html,并放在相应位置
此处博主创建名为“index.html”的文件,代码如下:
<!DOCTYPE html>
<html>
<head><meta name="viewport" content="initial-scale=1.0, user-scalable=no" /><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>BDMap Sample</title><style type="text/css">html{height:100%}body{height:100%;margin:0px;padding:0px}#container{height:100%} /* 确保地图容器占满QWebEngineView */</style><!-- 1. 加载百度地图 JS API v3(非WebGL,兼容性更好) --><script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=tSE1tEJjbMzrknZMAQgoj5A"></script><!-- 2. 加载QWebChannel(使用qrc资源路径,避免绝对路径问题) --><script type="text/javascript" src="qrc:/qtchannel/qwebchannel.js"></script>
</head>
<body><div id="container"></div><script type="text/javascript">// 3. 先初始化QWebChannel,再初始化地图(确保交互通道就绪)var JSInterface = null;new QWebChannel(qt.webChannelTransport, function(channel) {JSInterface = channel.objects.JSInterface; // 获取Qt注册的交互对象JSInterface.logFromJS("QWebChannel初始化成功"); // 通知Qt交互就绪// 4. QWebChannel就绪后,再初始化百度地图(避免阻塞)initBaiduMap();});// 5. 百度地图初始化函数(独立封装,逻辑清晰)function initBaiduMap() {try {if (typeof BMap === 'undefined') {console && console.error && console.error('BMap 未加载,检查网络或AK');if (JSInterface && JSInterface.logFromJS) JSInterface.logFromJS('BMap 未加载,可能是网络或AK问题');return;}var map = new BMap.Map("container");// 沈阳浑南区默认坐标(作为定位失败的备选)var defaultPoint = new BMap.Point(123.431982, 41.766281);// 初始化地图(先显示默认位置)map.centerAndZoom(defaultPoint, 15);map.enableScrollWheelZoom(true);// IP定位获取当前位置var geolocation = new BMap.Geolocation();geolocation.getCurrentPosition(function(r) {if (this.getStatus() == BMAP_STATUS_SUCCESS) {// 定位成功,移动到当前位置var currentPoint = r.point;map.panTo(currentPoint);// 添加当前位置标注var marker = new BMap.Marker(currentPoint);map.addOverlay(marker);// 显示定位信息var infoWindow = new BMap.InfoWindow("当前位置:<br/>经度:" + currentPoint.lng + "<br/>纬度:" + currentPoint.lat,{width: 200, height: 100, title: "定位成功"});marker.addEventListener("click", function() {map.openInfoWindow(infoWindow, currentPoint);});JSInterface.logFromJS("定位成功:(" + currentPoint.lng + "," + currentPoint.lat + ")");} else {// 定位失败,使用默认坐标(沈阳浑南)JSInterface.logFromJS("定位失败,错误码:" + this.getStatus() + ",使用默认位置");var marker = new BMap.Marker(defaultPoint);map.addOverlay(marker);}}, {enableHighAccuracy: true}); // 启用高精度定位window.map = map; // 将地图实例挂载到window,供addMarker使用JSInterface.logFromJS("百度地图初始化成功");} catch (e) {JSInterface.logFromJS("地图初始化异常:" + e.message);}
}// 6. 简化addMarker函数(移除无意义的alert,避免阻塞)function addMarker(lng, lat) {if (!window.map) {JSInterface.logFromJS("addMarker失败:地图未初始化");return;}var newPoint = new BMap.Point(lng, lat);var newMarker = new BMap.Marker(newPoint);window.map.addOverlay(newMarker);JSInterface.logFromJS("添加标注成功:(" + lng + "," + lat + ")");}// 全局错误日志window.onerror = function(message, source, lineno, colno, error) {try {if (JSInterface && JSInterface.logFromJS) {JSInterface.logFromJS('JS错误: ' + message + ' @' + source + ':' + lineno + ':' + colno);}} catch (e) {}};</script>
</body>
</html>
下面截图将告诉html中哪些地方需要修改:
四、配置相关资源文件
1、首先,找到自己相关路径下的qwebchannel.js文件(例如我的路径是E:\perfect\qt5.14.2\Examples\Qt-5.14.2\webchannel\shared\qwebchannel.js),将其放到你的工程文件根目录(也就是你各个类代码文件的位置,如下图)。(释:qwebchannel.js能够让你的Qt工程和JS文件进行通信)。
2、为了方便管理工程的静态资源文件(qwebchannel.js),将其打包到可执行程序中(打包成qrc文件),避免程序运行时依赖外部文件路径,提高项目可移植性和稳定性。步骤如下:
3、在html文件中添加你的qrc资源文件路径,便于Qt和JS通信。
4、创建Qt中的ui,并进行相关配置。(释:将Widget控件提升为QWebEngineView,是因为QWebEngineView支持网页渲染与交互能力。它支持加载 HTML、JS、CSS,实现网页交互。)
5、别忘记在.pro文件中加入下面这段代码(释:目的是为了引入Qt WebEngine模块,让项目能使用QWebEngineView等网页相关的类。):
QT += webenginewidgets
五、编写Qt代码
项目结构如下:
1、MagImageProDisplay.pro代码如下:
QT += core gui
QT += webenginewidgetsgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0# 交互类源文件
SOURCES += \jsinterface.cpp \main.cpp \mainwindow.cpp# 交互类头文件
HEADERS += \jsinterface.h \mainwindow.hFORMS += \mainwindow.ui# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += targetRESOURCES += \qwebchannelresources.qrc
2、创建JSInterface类(JSInterface 是一个桥梁类,用于实现 C++ 与 JavaScript 之间的双向通信),代码中有详细注释。
(1)JSInterface.h如下:
#ifndef JSINTERFACE_H
#define JSINTERFACE_H
#include <QObject>
#include <QDebug>class JSInterface : public QObject
{Q_OBJECT
public:explicit JSInterface(QObject *parent = nullptr);// (可添加供HTML调用的方法,如后续扩展功能)
public slots:void logFromJS(const QString &msg) {qDebug() << "[HTML Log]:" << msg; // 接收HTML日志,辅助调试}
};#endif // JSINTERFACE_H/*
JSInterface 是一个桥梁类,用于实现 C++ 与 JavaScript 之间的双向通信:
1、让网页中的 JS 代码能够调用 C++ 方法;
2、处理从网页发送过来的数据或命令;
3、在 C++ 端对来自网页的数据进行安全验证;
4、执行需要在 C++ 端处理的核心功能。如果去掉此类,WebChannel失去意义:
QWebChannel 需要注册 QObject 才能工作,没有注册对象,WebChannel 就只是一个空通道。
*/
(2)JSInterface.cpp如下:
#include "JSInterface.h"JSInterface::JSInterface(QObject *parent) : QObject(parent)
{}
3、MainWindow类。
(1)MainWindow.h如下:
#ifndef MAINWINDOW_H // 防止头文件重复包含的宏定义
#define MAINWINDOW_H // 定义主窗口头文件宏#include <QMainWindow>
#include <QWidget>
#include <QWebEngineHistory> // 包含Web引擎历史记录类头文件
#include <QWebEngineHistoryItem> // 包含Web引擎历史记录项类头文件
#include <QWebEnginePage> // 包含Web引擎页面类头文件
#include <QWebEngineView> // 包含Web引擎视图类头文件
#include <QtWebEngineWidgets/QtWebEngineWidgets> // 包含Web引擎部件模块头文件QT_BEGIN_NAMESPACE // 开始Qt命名空间
namespace Ui { class MainWindow; }
QT_END_NAMESPACE // 结束Qt命名空间class MainWindow : public QMainWindow
{Q_OBJECT // Qt元对象系统宏,支持信号槽机制public:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
(2)MainWindow.cpp如下:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "JSInterface.h" // 包含JavaScript交互接口头文件
#include <QFile> // 包含文件操作类头文件
#include <QUrl> // 包含URL处理类头文件MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QWebEnginePage *page = ui->BaiduMap->page(); // 获取Web引擎视图的页面对象QWebEngineProfile *profile = page->profile(); // 获取页面配置文件(未使用)// 处理网页特性权限请求(地理位置等),自动授予当前页面的定位权限connect(page, &QWebEnginePage::featurePermissionRequested, this,[page](const QUrl &securityOrigin, QWebEnginePage::Feature feature) {if (feature == QWebEnginePage::Geolocation) {page->setFeaturePermission(securityOrigin, feature, QWebEnginePage::PermissionGrantedByUser);}});// 启用跨域请求支持QWebEngineSettings *settings = page->settings(); // 获取页面设置对象settings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true); // 允许本地内容访问远程URLsettings->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, true); // 允许本地内容访问文件URLsettings->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, true); // 允许运行不安全内容(混合内容)settings->setAttribute(QWebEngineSettings::WebGLEnabled, false); // 关闭WebGL,避免显卡驱动导致崩溃settings->setAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled, false); // 关闭2D加速// 初始化交互对象(独立于QWebEngineView),即 创建JavaScript交互接口对象JSInterface *jsInterface = new JSInterface(this);// 配置QWebChannel(必须在load之前完成)QWebChannel *webChannel = new QWebChannel(this); // 创建Web通道对象webChannel->registerObject("JSInterface", jsInterface); // 注册交互对象到Web通道,JavaScript中可通过此名称访问ui->BaiduMap->page()->setWebChannel(webChannel); // 将Web通道设置到页面// 加载HTML(确保路径正确,使用QUrl::fromLocalFile避免路径解析错误)QString htmlPath = QCoreApplication::applicationDirPath() + "/html/index.html"; // 构建HTML文件路径QFile htmlFile(htmlPath); // 创建文件对象// 检查文件是否存在if (!htmlFile.exists()) {qDebug() << "[Error] HTML文件不存在:" << htmlPath;return;}QUrl htmlUrl = QUrl::fromLocalFile(htmlPath); // 将文件路径转换为URLui->BaiduMap->page()->load(htmlUrl); // 加载HTML文件qDebug() << "[Info] 加载HTML路径:" << htmlUrl.toString(); // 输出加载信息}MainWindow::~MainWindow()
{delete ui;
}
六、可能遇见的问题
按照上面的流程是不会出现下面这些问题的,因为博主就是解决了下面的问题才写出上面的流程和代码的,加上这章节内容只是为了记录一下,万一某天某刻某人遇到下面问题了呢!
问题一:可能出现下面图片报错。
解决:建议重新注册一个百度的api,因为KEY可能被删除了(可以去看第二章节如何获取密钥的)。
问题二:地图快要加载出来的时候,然后整个地图的ui闪退没了(下图)。
解决:检查一下HTML中QWebChannel与百度地图API的加载顺序,加载顺序不当,会触发 Qt WebEngine 控件崩溃。所以一定要先确保QWebChannel通信通道初始化完成并注册好交互对象(JSInterface),避免页面JS运行时通道还没建好。
问题三:地图的内容一直加载不出来(如下图)
解决:保留地理位置自动授权(主要还是权限没有),(后面都是次要)将前端从 BMapGL 切换为 BMap v3(非 WebGL),避免显卡驱动/QtWebEngine WebGL 崩溃。
最后,如有不足和错误的地方,期待私信指正!