PyQt5中QWebEngineView.runJavaScript执行Mapbox JS报错求助
问题描述
我使用PyQt5开发Python程序时,调用runJavaScript()方法无法在Mapbox GL JS的HTML文档上正确执行JavaScript代码。
HTML代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Display a map on a webpage</title> <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"> <link href="https://api.mapbox.com/mapbox-gl-js/v2.10.0/mapbox-gl.css" rel="stylesheet"> <script src="https://api.mapbox.com/mapbox-gl-js/v2.10.0/mapbox-gl.js"></script><script src="qrc:///qtwebchannel/qwebchannel.js"></script> <style> body { margin: 0; padding: 0; } #map { position: absolute; top: 0; bottom: 0; width: 100%; } </style> </head> <body> <div id="map"></div> <script> mapboxgl.accessToken = 'pk.eyJ1IjoidmxhZGlra2lyIiwiYSI6ImNsNno2dnN3cjAxamYzbm4xeDhxa2xuY2oifQ.HhDTHZglHlDNte7XwGZ1Xg'; const map = new mapboxgl.Map({ container: 'map', // container ID style: 'mapbox://styles/mapbox/streets-v11', // style URL center: [-74.5, 40], // starting position [lng, lat] zoom: 9, // starting zoom projection: 'globe' // display the map as a 3D globe }); map.on('style.load', () => { map.setFog({}); // Set the default atmosphere style }); </script> </body> </html>
Python代码片段
# 创建QWebEngineView组件 self.mapView = QtWebEngineWidgets.QWebEngineView() mapSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) mapSizePolicy.setHeightForWidth(self.mapView.sizePolicy().hasHeightForWidth()) self.mapView.setSizePolicy(mapSizePolicy) self.mapView.setObjectName("mapView") self.detstartpointMapLayout.addWidget(self.mapView) # 读取HTML并加载到组件 with open('mapboxjs.html', 'r') as file: mapHTML = file.read() self.mapView.setHtml(mapHTML) # 执行JavaScript代码(失败) self.mapView.page().runJavaScript("const marker1 = new mapboxgl.Marker().setLngLat([12.554729, 55.70651]).addTo(map);")
错误信息
js: Uncaught ReferenceError: mapboxgl is not defined .
注:这段JavaScript代码在Firefox控制台执行正常,推测是runJavaScript()或QWebEngineView未识别到HTML头部通过<script>标签导入的库。
解决方案
核心问题是调用runJavaScript()的时机过早,此时HTML中的Mapbox JS库还未加载完成,页面也未初始化完毕。浏览器中能正常执行是因为手动操作时页面已完全加载,而PyQt中直接调用会跳过加载等待阶段。
方法一:监听页面加载完成信号
利用QWebEngineView的loadFinished信号,等页面完全加载后再执行JavaScript代码:
# 替换原runJavaScript调用,绑定加载完成信号 self.mapView.loadFinished.connect(self.on_map_loaded) def on_map_loaded(self, success): if success: # 页面加载成功后执行目标JS js_code = "const marker1 = new mapboxgl.Marker().setLngLat([12.554729, 55.70651]).addTo(map);" self.mapView.page().runJavaScript(js_code)
方法二:通过Qt WebChannel实现回调交互
适合复杂场景的交互需求,通过HTML主动通知Python地图初始化完成:
- 修改HTML代码,添加WebChannel逻辑:
<script> mapboxgl.accessToken = 'pk.eyJ1IjoidmxhZGlra2lyIiwiYSI6ImNsNno2dnN3cjAxamYzbm4xeDhxa2xuY2oifQ.HhDTHZglHlDNte7XwGZ1Xg'; const map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/streets-v11', center: [-74.5, 40], zoom: 9, projection: 'globe' }); map.on('style.load', () => { map.setFog({}); // 地图初始化完成后通知Python端 new QWebChannel(qt.webChannelTransport, function(channel) { window.pyObj = channel.objects.pyObj; pyObj.mapReady(); }); }); </script>
- Python端创建通信对象并绑定信号:
from PyQt5.QtWebChannel import QWebChannel from PyQt5.QtCore import QObject, pyqtSignal class MapBridge(QObject): mapReady = pyqtSignal() # 在初始化mapView的代码中添加: self.bridge = MapBridge() channel = QWebChannel() channel.registerObject("pyObj", self.bridge) self.mapView.page().setWebChannel(channel) # 绑定地图就绪信号 self.bridge.mapReady.connect(self.on_map_ready) def on_map_ready(self): js_code = "const marker1 = new mapboxgl.Marker().setLngLat([12.554729, 55.70651]).addTo(map);" self.mapView.page().runJavaScript(js_code)
额外注意事项
- 原Python代码中
self.mapView = QtWebEngineWidgets.a是笔误,正确写法为QtWebEngineWidgets.QWebEngineView(),已在示例中修正。 - 使用
setHtml()加载本地HTML时,需确保网络连接正常,以加载Mapbox的外部资源。
内容的提问来源于stack exchange,提问作者Mineges
相关产品推荐
相关产品推荐

