PyQt5中使用ipyleaflet替代folium时出现URL构造错误求助
解决PyQt5中嵌入ipyleaflet地图时的URL构造错误
问题背景
- 大学项目需求:从微控制器获取GPS数据,通过PyQt5的Web组件展示位置
- 最初使用folium,但移动标记时必须重新渲染地图,操作繁琐
- 改用ipyleaflet实现标记的动态移动/删除,但ipyleaflet没有folium的
_repr_html_()方法直接转换地图为HTML - 参考方案生成的HTML在浏览器中可正常运行,但在PyQt5中运行时出现JS错误:
js: Uncaught TypeError: Failed to construct 'URL': Invalid URL
用户代码
主程序代码
import sys import json from ipyleaflet import Map, Marker, LayersControl, basemaps from ui import * from PyQt5.QtWidgets import QDialog, QApplication from ipywidgets import HTML, IntSlider from ipywidgets.embed import embed_minimal_html, embed_data class MyForm(QDialog): def __init__(self): super().__init__() self.ui = Ui_Dialog() self.ui.setupUi(self) html_template = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>IPyWidget export</title> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js" integrity="sha256-Ae2Vz/4ePdIu6ZyI/5ZGsYnb+m0JlOmKPjt6XZ9JJkA=" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/@jupyter-widgets/html-manager@^1.0.1/dist/embed-amd.js" crossorigin="anonymous"></script> <script type="application/vnd.jupyter.widget-state+json"> {manager_state} </script> <div id="ipyleaflet-map"> <script type="application/vnd.jupyter.widget-view+json"> {widget_views} </script> </div> </body> </html> """ self.m = Map(center=(24.179509715480204, 12.66299706912081), zoom=16) marker = Marker(location=(24.179509715480204, 12.66299706912081), tooltip="woohhooo") self.m.add_layer(marker) data = embed_data(views=[self.m]) manager_state = json.dumps(data['manager_state']) widget_views = [json.dumps(data['view_specs'])] rendered_template = html_template.format(manager_state=manager_state, widget_views=widget_views) self.ui.widget.setHtml(rendered_template) if __name__ == '__main__': app = QApplication(sys.argv) w = MyForm() w.show() sys.exit(app.exec_())
UI代码
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'untitled.ui' # # Created by: PyQt5 UI code generator 5.15.0 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtWebEngineWidgets class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") Dialog.resize(638, 400) self.widget = QtWebEngineWidgets.QWebEngineView(Dialog) self.widget.setGeometry(QtCore.QRect(9, 9, 621, 390)) self.widget.setObjectName("widget") self.retranslateUi(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
错误原因
使用setHtml()直接加载HTML字符串时,PyQt5的QWebEngineView会将页面视为无基础URL的本地内容,而ipyleaflet嵌入的JS代码在加载依赖资源时,会尝试基于当前页面URL构造新的URL,没有合法基础URL时就会触发"Invalid URL"错误。
解决方案
方法一:保存HTML到本地文件后加载
将生成的HTML内容保存为本地文件,再通过load()方法加载文件的本地URL,为页面提供合法的基础URL:
修改主程序中加载HTML的部分:
import os # 需要新增导入os模块 # ... 原有代码生成rendered_template ... # 保存HTML到本地文件 with open("map.html", "w", encoding="utf-8") as f: f.write(rendered_template) # 加载本地文件,使用file://协议 self.ui.widget.load(QtCore.QUrl.fromLocalFile(os.path.abspath("map.html")))
方法二:为setHtml指定基础URL
调用setHtml()时,传入第二个参数指定本地目录作为基础URL,让JS能正确构造资源路径:
修改主程序中加载HTML的部分:
import os # 需要新增导入os模块 # ... 原有代码生成rendered_template ... # 指定当前目录为基础URL base_url = QtCore.QUrl.fromLocalFile(os.path.abspath("./")) self.ui.widget.setHtml(rendered_template, base_url)
验证
修改后,PyQt5的Web组件即可正常加载ipyleaflet地图,支持标记的动态移动、删除等操作。
内容的提问来源于stack exchange,提问作者Dubaja
相关产品推荐
相关产品推荐

