如何将PyQt5生成的经纬度列表传递给Leaflet.js的JavaScript文件?
问题描述
我正在用PyQt5开发一款桌面应用,应用里包含Tab Widget,其中有个“Start”按钮。点击按钮后会生成一组经纬度列表,我希望把这个列表传给使用Leaflet.js的JavaScript文件,用来绘制地图。以下是我的Python代码:
import sys from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QTabWidget, QPushButton, QApplication) class App(QMainWindow): def __init__(self): super().__init__() self.title = 'Delivering & Tracking' self.setWindowTitle(self.title) self.tab_widget = MyTabWidget(self) self.setCentralWidget(self.tab_widget) self.showFullScreen() class MyTabWidget(QWidget): def __init__(self, parent): super(QWidget, self).__init__(parent) self.layout = QVBoxLayout(self) #Initialize tab screen self.tabs = QTabWidget() self.map_tab = Maps() self.tabs.resize(300,200) self.tabs.addTab(self.map_tab,QIcon("./icons and images/tab_map.png"), ('Map')) self.layout.addWidget(self.tabs) self.setLayout(self.layout) class Maps(QWidget): def __init__(self): super().__init__() self.startButton() def startButton(self): button = QPushButton("Start", self) button.move(655, 415) button.clicked.connect(self.map) self.show() def map(self): #A list is generated in this function. latlon = [ # 示例经纬度,实际是动态生成的 [39.9042, 116.4074], [31.2304, 121.4737], [23.1291, 113.2644] ] if __name__ == '__main__': app = QApplication(sys.argv) ex = App() sys.exit(app.exec_())
请问怎么把map函数里生成的latlon列表传递给JavaScript文件呢?
解决方案
嘿,这个问题其实很常见,PyQt5和前端JS交互有两种主流方式,我给你详细讲讲,你可以根据自己的需求选:
方案1:直接用evaluateJavaScript传数据(简单场景首选)
这种方式适合单向传递数据(Python→JS),直接调用JS里的函数并把数据传进去,代码量少,容易实现。
第一步:修改你的Python代码
首先要导入QWebEngineView,用它来加载包含Leaflet的HTML页面,然后在点击按钮时把经纬度数据转成JSON传给JS:
import sys import json from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QTabWidget, QPushButton, QApplication) from PyQt5.QtWebEngineWidgets import QWebEngineView from PyQt5.QtCore import QUrl, QIcon # 别忘了导入QIcon class App(QMainWindow): def __init__(self): super().__init__() self.title = 'Delivering & Tracking' self.setWindowTitle(self.title) self.tab_widget = MyTabWidget(self) self.setCentralWidget(self.tab_widget) self.showFullScreen() class MyTabWidget(QWidget): def __init__(self, parent): super(QWidget, self).__init__(parent) self.layout = QVBoxLayout(self) self.tabs = QTabWidget() self.map_tab = Maps() self.tabs.resize(300,200) self.tabs.addTab(self.map_tab, QIcon("./icons and images/tab_map.png"), 'Map') self.layout.addWidget(self.tabs) self.setLayout(self.layout) class Maps(QWidget): def __init__(self): super().__init__() # 改用布局管理控件,比move()更适配不同窗口大小 self.layout = QVBoxLayout(self) self.startButton() # 初始化Web视图加载地图HTML self.web_view = QWebEngineView() # 替换成你的map.html文件路径,一定要用绝对路径或者正确的相对路径 self.web_view.load(QUrl.fromLocalFile("./map.html")) self.layout.addWidget(self.web_view) self.setLayout(self.layout) def startButton(self): button = QPushButton("Start", self) button.clicked.connect(self.map) self.layout.addWidget(button) def map(self): # 这里是你的动态生成经纬度逻辑,我用示例数据代替 latlon = [ [39.9042, 116.4074], [31.2304, 121.4737], [23.1291, 113.2644] ] # 把Python列表转成JSON字符串,JS能直接解析 latlon_json = json.dumps(latlon) # 调用JS里的drawMarkers函数,把数据传进去 self.web_view.page().runJavaScript(f"drawMarkers({latlon_json});") if __name__ == '__main__': app = QApplication(sys.argv) ex = App() sys.exit(app.exec_())
第二步:写包含Leaflet的HTML文件(map.html)
在HTML里定义drawMarkers函数,接收Python传来的经纬度,然后在地图上绘制标记:
<!DOCTYPE html> <html> <head> <title>Delivery Map</title> <!-- 引入Leaflet的CSS和JS --> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> <style> /* 给地图设置高度,不然显示不出来 */ #map { height: 600px; width: 100%; } </style> </head> <body> <div id="map"></div> <script> // 初始化地图,默认显示中国区域 const map = L.map('map').setView([35.8617, 104.1954], 5); // 添加OpenStreetMap的瓦片图层 L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(map); // 定义接收Python数据的函数 function drawMarkers(latlonList) { // 先清除之前的标记,避免重复绘制 map.eachLayer(layer => { if (layer instanceof L.Marker) { map.removeLayer(layer); } }); // 遍历经纬度列表,添加标记和弹窗 latlonList.forEach(latlon => { L.marker(latlon).addTo(map) .bindPopup(`坐标:${latlon[0]}, ${latlon[1]}`); }); // 自动调整地图视野,让所有标记都显示在视图内 const bounds = L.latLngBounds(latlonList); map.fitBounds(bounds); } </script> </body> </html>
方案2:用QWebChannel实现双向通信(复杂场景推荐)
如果你的应用需要JS给Python回传数据(比如用户点击地图标记触发Python逻辑),那QWebChannel是更规范的选择,它支持双向信号槽通信。
第一步:修改Python代码,配置QWebChannel
import sys import json from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QTabWidget, QPushButton, QApplication) from PyQt5.QtWebEngineWidgets import QWebEngineView from PyQt5.QtCore import QUrl, QIcon, QObject, pyqtSlot, pyqtSignal # 定义一个桥接类,用来和JS通信 class MapBridge(QObject): # 定义一个信号,当有新经纬度数据时发送给JS newLatLonData = pyqtSignal(str) @pyqtSlot() def sendLatLonData(self): # 这里是你的动态生成经纬度逻辑 latlon = [ [39.9042, 116.4074], [31.2304, 121.4737], [23.1291, 113.2644] ] # 转成JSON字符串发送 self.newLatLonData.emit(json.dumps(latlon)) class Maps(QWidget): def __init__(self): super().__init__() self.layout = QVBoxLayout(self) self.startButton() self.web_view = QWebEngineView() # 初始化QWebChannel self.bridge = MapBridge() self.web_view.page().setWebChannel(self.bridge) # 加载带QWebChannel的HTML文件 self.web_view.load(QUrl.fromLocalFile("./map_channel.html")) self.layout.addWidget(self.web_view) self.setLayout(self.layout) def startButton(self): button = QPushButton("Start", self) # 点击按钮时调用桥接类的方法发送数据 button.clicked.connect(self.bridge.sendLatLonData) self.layout.addWidget(button) # App和MyTabWidget类和之前一样,这里省略...
第二步:编写带QWebChannel的HTML文件(map_channel.html)
<!DOCTYPE html> <html> <head> <title>Delivery Map with WebChannel</title> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> <!-- 引入QWebChannel的JS文件,这个文件在PyQt安装目录里,复制到项目根目录 --> <script src="qwebchannel.js"></script> <style> #map { height: 600px; width: 100%; } </style> </head> <body> <div id="map"></div> <script> const map = L.map('map').setView([35.8617, 104.1954], 5); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(map); // 初始化QWebChannel,连接Python的桥接对象 new QWebChannel(qt.webChannelTransport, function(channel) { const mapBridge = channel.objects.mapBridge; // 监听Python发送的newLatLonData信号 mapBridge.newLatLonData.connect(function(latlonJson) { const latlonList = JSON.parse(latlonJson); // 绘制标记的逻辑和之前一样 map.eachLayer(layer => { if (layer instanceof L.Marker) { map.removeLayer(layer); } }); latlonList.forEach(latlon => { L.marker(latlon).addTo(map) .bindPopup(`坐标:${latlon[0]}, ${latlon[1]}`); }); map.fitBounds(L.latLngBounds(latlonList)); }); }); </script> </body> </html>
注意事项
qwebchannel.js文件可以在你的PyQt安装路径里找到,比如Python/Lib/site-packages/PyQt5/Qt/bin/qwebchannel.js,把它复制到项目目录下,和HTML文件放在一起。- 不管用哪种方案,都要确保HTML文件的路径正确,用
QUrl.fromLocalFile加载本地文件,避免跨域问题。
内容的提问来源于stack exchange,提问作者Aloysius Samuel
相关产品推荐
相关产品推荐

