作者:求一个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 += webenginewidgets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += 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.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += \
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); // 允许本地内容访问远程URL
settings->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, true); // 允许本地内容访问文件URL
settings->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); // 将文件路径转换为URL
ui->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 崩溃。
最后,如有不足和错误的地方,期待私信指正!