You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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: '&copy; 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: '&copy; 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 22:04:05