. 解决QT使用QWebEngineView加载不出网页问题
这次项目需要用到qt去调高德地图进行显示,查阅资料后知道了qt可以用QWebEngineView类打开html文件并进行显示但是途中遇到了地图加载不出来的问题。但将源代码发给其他人之后,发现别人可以打开,但时间略长大概需要十秒左右,怀疑是QT版本问题,我现在用的版本是QT6.6.7,此版本加载不出地图显示,但可以加载出经纬度的文字和显示框。之后重新切换QT版本至QT5.15.2之后,生成方式选择Release之后,问题解决,地图可以在一秒左右加载出来。
ps:代码中最好加上一句QNetworkProxyFactory::setUseSystemConfiguration(false);这个代码是让qt不使用默认的代理,我测试下来,不加这句代码会稍微慢个两秒钟,也可能是偶然问题,现在不太清楚,但最好加上,避免奇奇怪怪的问题。
下面是我的显示代码:(这篇博客写完我会去研究qt与html的通信)(已更新)
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget){ ui->setupUi(this); QNetworkProxyFactory::setUseSystemConfiguration(false); mainMap_view = new QWebEngineView(this); mainMap_view->setParent(ui->frame); mainMap_view->page()->load(QUrl("qrc:/new/prefix1/Qtmap/GD.html")); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(mainMap_view);}Widget::~Widget(){ delete ui;}
更新于2024年1月23日,现在成功的在qt界面点击,html获得的经纬度信息反馈回qt。先看一下效果:
实现qt与html网页基础通信
好了,现在给大家介绍一下实现逻辑。(其实我也有点懵,因为我不懂html的代码,功能的实现GPT帮了很大的忙。不得不插一句题外话,做纯软的话GPT真的太牛了,我做单片机的时候很多问题因为可能涉及硬件他给的答案只能参考。纯软就不一样了。好了继续)首先在qt的代码逻辑是这样:
第一步:定义一个基于QObject类的对象,GPT给的名字是MapInteraction那咱就用这个名字。这个对象类定义一个槽函数用来接收html发过来的经纬度信息。
.h文件内容和.cpp内容
#ifndef MAPINTERACTION_H#define MAPINTERACTION_H#include <QObject>#include <QCoreApplication>#include <QWebEngineView>#include <QWebChannel>#include <QJsonObject>#include <QJsonValue>#include <QDebug>class MapInteraction : public QObject{ Q_OBJECTpublic: explicit MapInteraction(QObject *parent = nullptr);public slots: void handleMapClick(const QJsonObject &position);signals:};#endif // MAPINTERACTION_H
#include "mapinteraction.h"MapInteraction::MapInteraction(QObject *parent) : QObject{parent}{}void MapInteraction::handleMapClick(const QJsonObject &position){ // 处理地图点击事件 double latitude = position["latitude"].toDouble(); double longitude = position["longitude"].toDouble(); // 在这里可以执行任何您想要的操作,例如将经纬度信息发送到Qt应用程序的其他部分 qDebug() << "Clicked on map. Longitude:" << longitude<<", latitude: "<<latitude;}
第二步我们需要注册一个QWebchanle类的对象用来和html之间通信。
1.根据上一步创建的类,new一个对象,我这里叫mapInteraction。mapInteraction = new MapInteraction(this);
2.new一个QWebchanle类的对象,我这里叫webchanle。然后我们把第一步创建的对象mapInteraction注册进webchanle。 webchanle->registerObject(QStringLiteral("mapInteraction"), mapInteraction);
3.翻看最上面的代码我们这里有个网页的类mainMap_view。然后我们把这个对象和webchanle 关联起来。mainMap_view->page()->setWebChannel(webchanle);
下面是完整cpp和h代码和上一步网页加载和在一起了
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget){ ui->setupUi(this); webchanle = new QWebChannel(this); mapInteraction = new MapInteraction(this); webchanle->registerObject(QStringLiteral("mapInteraction"), mapInteraction); QNetworkProxyFactory::setUseSystemConfiguration(false); layout = new QVBoxLayout(this); mainMap_view = new QWebEngineView(this); mainMap_view->setParent(ui->frame); mainMap_view->page()->load(QUrl("qrc:/new/prefix1/Qtmap/GD.html")); mainMap_view->page()->setWebChannel(webchanle); layout->addWidget(mainMap_view);}Widget::~Widget(){ delete ui; delete mainMap_view; delete layout;}
#ifndef WIDGET_H#define WIDGET_H#include <QWidget>#include <QtWebEngineWidgets>#include <QWebEngineHistory>#include <QWebEngineHistoryItem>#include <QWebEnginePage>#include <QWebEngineView>#include <QtWebEngineWidgets/QtWebEngineWidgets>#include <QWebChannel>#include "mapinteraction.h"class MapInteraction;QT_BEGIN_NAMESPACEnamespace Ui {class Widget;}QT_END_NAMESPACEclass Widget : public QWidget{ Q_OBJECTpublic: Widget(QWidget *parent = nullptr); ~Widget(); QWebChannel *webchanle; QWebEngineView *mainMap_view; QVBoxLayout *layout; MapInteraction *mapInteraction;private: Ui::Widget *ui;};#endif // WIDGET_H
好了。qt内部的工作做完了接下来是html。
html中我首先是去高德示例中心中找到了一个基础的示例代码是鼠标点击获得经纬度。在这个基础上增加了鼠标点击出现图标和图标之间连线。这两个功能我就不给大家介绍了,因为我也不懂。这部分工作都是GPT帮助做的。我主要介绍一下html里面与qt通信相关的工作。
首先去qt的安装目录里搜索qwebchannel.js。把这个文件复制到qt'的资源文件夹里
第二部在html文件里添加这个文件 <script src="./qwebchannel.js"></script>
好了然后就可以开始了。
首先存储经纬度我们需要一个全局的数组变量。var position = [];
需要一个与qt通信相关的变量var qtChannel;
在鼠标点击的函数里获取经纬度并把数据传给qt
然后再定义一个window.onload
事件。当html页面的资源文件加载完成时。调用这个函数去访问qt是否存在并建立一个通道与qt进行通信。
至此。html与qt的基础通信就做好了。我把我的html完整代码贴出来大家如果懂一点html的话可以参考一下
<!doctype html><html><head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width"> <title>鼠标拾取地图坐标</title> <link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css" /> <script type="text/javascript" src="https://cache.amap.com/lbs/static/addToolbar.js"></script></head><style type="text/css"> html, body { width: 100%; height: 100%; margin: 0px; } .map { height: 100%; width: 100%; float: left; }</style><body> <div id="container" class="map"></div> <div class="input-card"> <h4>左击获取经纬度:</h4> <div class="input-item"> <input type="text" readonly="true" id="lnglat"> </div> </div> <script src="https://webapi.amap.com/maps?v=2.0&key=您的key值&plugin=AMap.Autocomplete"></script> <script src="./qwebchannel.js"></script> <script type="text/javascript"> var markers = []; var position = []; var polyline; var qtChannel; var map = new AMap.Map("container", { resizeEnable: true, zoom: 11, }); // 用线连接所有标记点的函数 function connectMarkers() { var polylinePoints = markers.map(function (marker) { return marker.getPosition(); }); // 清除现有的折线 if (polyline) { polyline.setMap(null); polyline = null; } // 检查是否有多个标记点 if (polylinePoints.length > 1) { // 创建连接标记点的折线 polyline = new AMap.Polyline({ path: polylinePoints, strokeColor: "#3366FF", // 线的颜色 strokeOpacity: 1, // 线的透明度 strokeWeight: 15, // 线的宽度 map: map }); } } function deleteMarker(index) { if (index >= 0 && index < markers.length) { markers[index].setMap(null); // 从地图上移除标记点 markers.splice(index, 1); // 从数组中移除标记点 // 更新连接的线 if (markers.length > 1) { if (polyline) { // 清除现有的折线 polyline.setMap(null); polyline = null; // 将对象设置为 null,释放内存 } // 重新连接标记点 connectMarkers(); } else if (markers.length === 1) { // 如果只剩一个点,清除折线 if (polyline) { polyline.setMap(null); polyline = null; } } } } map.on('click', function (e) { // 在点击位置创建标记 document.getElementById("lnglat").value = e.lnglat.getLng() + ',' + e.lnglat.getLat() var marker = new AMap.Marker({ icon: "https://a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png", anchor: 'bottom-center', // 设置锚点在图标底部中心 position: e.lnglat, map: map }); // 将标记添加到某个数组,以便后续引用 markers.push(marker); // 连接标记点 connectMarkers(); position = { latitude: e.lnglat.getLat(), longitude: e.lnglat.getLng() }; qtChannel.mapInteraction.handleMapClick(position); }); window.onload = function () { if (typeof qt != 'undefined') { new QWebChannel(qt.webChannelTransport, function (channel) { qtChannel = channel.objects; }); } else { alert("qt对象获取失败!"); } }; function getMarkerFromPosition(position) {//找到最近标记点的索引号 var minDistance = Infinity; var closestMarker = null; markers.forEach(function (marker) { var distance = marker.getPosition().distance(position); if (distance < minDistance) { minDistance = distance; closestMarker = marker; } }); return closestMarker; } map.on('rightclick', function (e) { // 获取右键点击位置的标记 var clickedMarker = getMarkerFromPosition(e.lnglat); // 删除指定标记 if (clickedMarker) { deleteMarker(markers.indexOf(clickedMarker)); } }); </script></body></html>
这份代码里有部分功能我没做介绍因为包括鼠标点击添加坐标点和坐标点连线,坐标点删除和删除后重新连线的功能,所以可能看着比较杂乱,各位可以配合着刚刚的截图和代码一起分析。(记得Key值填自己的)祝各位工作顺利。下次再见。