1

我想用 Python 显示一个 2D 地图,然后在 Python 代码中对游标的坐标做一些事情。但是,我无法获得 Python 部分的坐标。这是我的代码:

from PyQt5.QtCore import *
from PyQt5.QtGui import *

from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtWidgets import QWidget,QVBoxLayout, QApplication
from PyQt5.QtWebChannel import QWebChannel
import bs4

maphtml = '''

<!DOCTYPE HTML>
<!DOCTYPE HTML>
<html>
  <head>
    <meta name="robots" content="index, all" />    
    <script src="qrc:///qtwebchannel/qwebchannel.js"></script>
    <title>WebGL Earth API - Side-by-side - Basic Leaflet compatibility</title>
    <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" />
    <script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
    <script src="http://www.webglearth.com/v2/api.js"></script>
    <script>
      var backend;
        new QWebChannel(qt.webChannelTransport, function (channel) {
            backend = channel.objects.backend;
        });
      function init() {
        var m = {};

        start_(L, 'L');
        start_(WE, 'WE');

        function start_(API, suffix) {
          var mapDiv = 'map' + suffix;
          var map = API.map(mapDiv, {
            center: [51.505, -0.09],
            zoom: 4,
            dragging: true,
            scrollWheelZoom: true,
            proxyHost: 'http://srtm.webglearth.com/cgi-bin/corsproxy.fcgi?url='
          });
          m[suffix] = map;

          //Add baselayer
          API.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
            attribution: '© OpenStreetMap contributors'
          }).addTo(map);

          //Add TileJSON overlay
          var json = {"profile": "mercator", "name": "Grand Canyon USGS", "format": "png", "bounds": [-112.26379395, 35.98245136, -112.10998535, 36.13343831], "minzoom": 10, "version": "1.0.0", "maxzoom": 16, "center": [-112.18688965, 36.057944835, 13], "type": "overlay", "description": "", "basename": "grandcanyon", "tilejson": "2.0.0", "sheme": "xyz", "tiles": ["http://tileserver.maptiler.com/grandcanyon/{z}/{x}/{y}.png"]};
          if (API.tileLayerJSON) {
            var overlay2 = API.tileLayerJSON(json, map);
          } else {
            //If not able to display the overlay, at least move to the same location
            map.setView([json.center[1], json.center[0]], json.center[2]);
          }

          //Add simple marker
          var marker = API.marker([json.center[1], json.center[0]]).addTo(map);
          marker.bindPopup(suffix, 50);
          marker.openPopup();

          //Print coordinates of the mouse
          map.on('mousemove', function(e) {
            document.getElementById('coords').innerHTML = e.latlng.lat + ', ' + e.latlng.lng;
            backend.print(e.latlng.lat)
          });
        }

        //Synchronize view
        m['L'].on('move', function(e) {
          var center = m['L'].getCenter();
          var zoom = m['L'].getZoom();
          m['WE'].setView([center['lat'], center['lng']], zoom);
        });
      }
    </script>
    <style>
      html, body{padding: 0; margin: 0; overflow: hidden;}
      #mapL, #mapWE {position:absolute !important; top: 0; right: 0; bottom: 0; left: 0;
                     background-color: #fff; position: absolute !important;}
      #mapL {right: 0%;}
      #mapWE {left: 100%;}
      #coords {position: absolute; bottom: 0;}
    </style>
  </head>
  <body onload="javascript:init()">
    <div id="mapL"></div>
    <div id="mapWE"></div>
    <div id="coords"></div>
  </body>
</html>
'''

class Browser(QApplication):
    def __init__(self):
        QApplication.__init__(self, [])
        self.window = QWidget()
        self.window.setWindowTitle("Serial GPS Emulator");

        self.web = QWebEngineView(self.window)

        self.web.setHtml(maphtml)
        self.layout = QVBoxLayout(self.window)
        self.layout.addWidget(self.web)

        self.window.show()
        self.exec()

Browser()

如果代码保留在一个文件中会很好,但如果完全不可能在一次拆分中完成它是可以接受的。我想解决 mny 问题的第一步是从 HTML/JS 部分调用一个函数,因为 backend.print("test") 也不起作用。我还注意到 self.exec() 阻止了其余代码,有没有办法在地图运行时执行任何其他代码?谢谢!

4

1 回答 1

2

我看不到您在代码中的哪个位置通过了后端。

在这种情况下,您必须创建一个Backend可以注入的类,并且对于要调用的方法,它必须是一个插槽,为此您必须使用pyqtSlot()设置,它接收的参数取决于您发送的参数,在这种情况下的e.latlng是一个QJsonValue。在插槽中,您必须分离必要的部件。

import sys

from PyQt5.QtCore import pyqtSlot, QObject, QJsonValue, pyqtSignal, QTimer
from PyQt5.QtWebChannel import QWebChannel
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication

maphtml = '''

<!DOCTYPE HTML>
<!DOCTYPE HTML>
<html>
  <head>
    <meta name="robots" content="index, all" />    
    <script src="qrc:///qtwebchannel/qwebchannel.js"></script>
    <title>WebGL Earth API - Side-by-side - Basic Leaflet compatibility</title>
    <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" />
    <script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
    <script src="http://www.webglearth.com/v2/api.js"></script>
    <script>
      var backend;
        new QWebChannel(qt.webChannelTransport, function (channel) {
            backend = channel.objects.backend;
            console.log(backend);
        });
      function init() {
        var m = {};

        start_(L, 'L');
        start_(WE, 'WE');

        function start_(API, suffix) {
          var mapDiv = 'map' + suffix;
          var map = API.map(mapDiv, {
            center: [51.505, -0.09],
            zoom: 4,
            dragging: true,
            scrollWheelZoom: true,
            proxyHost: 'http://srtm.webglearth.com/cgi-bin/corsproxy.fcgi?url='
          });
          m[suffix] = map;

          //Add baselayer
          API.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
            attribution: '© OpenStreetMap contributors'
          }).addTo(map);

          //Add TileJSON overlay
          var json = {"profile": "mercator", "name": "Grand Canyon USGS", "format": "png", "bounds": [-112.26379395, 35.98245136, -112.10998535, 36.13343831], "minzoom": 10, "version": "1.0.0", "maxzoom": 16, "center": [-112.18688965, 36.057944835, 13], "type": "overlay", "description": "", "basename": "grandcanyon", "tilejson": "2.0.0", "sheme": "xyz", "tiles": ["http://tileserver.maptiler.com/grandcanyon/{z}/{x}/{y}.png"]};
          if (API.tileLayerJSON) {
            var overlay2 = API.tileLayerJSON(json, map);
          } else {
            //If not able to display the overlay, at least move to the same location
            map.setView([json.center[1], json.center[0]], json.center[2]);
          }

          //Add simple marker
          var marker = API.marker([json.center[1], json.center[0]]).addTo(map);
          marker.bindPopup(suffix, 50);
          marker.openPopup();

          //Print coordinates of the mouse
          map.on('mousemove', function(e) {
            document.getElementById('coords').innerHTML = e.latlng.lat + ', ' + e.latlng.lng;
            backend.print(e.latlng)
          });
        }

        //Synchronize view
        m['L'].on('move', function(e) {
          var center = m['L'].getCenter();
          var zoom = m['L'].getZoom();
          m['WE'].setView([center['lat'], center['lng']], zoom);
        });
      }
    </script>
    <style>
      html, body{padding: 0; margin: 0; overflow: hidden;}
      #mapL, #mapWE {position:absolute !important; top: 0; right: 0; bottom: 0; left: 0;
                     background-color: #fff; position: absolute !important;}
      #mapL {right: 0%;}
      #mapWE {left: 100%;}
      #coords {position: absolute; bottom: 0;}
    </style>
  </head>
  <body onload="javascript:init()">
    <div id="mapL"></div>
    <div id="mapWE"></div>
    <div id="coords"></div>
  </body>
</html>
'''


class Backend(QObject):
    positionChanged = pyqtSignal(float, float)

    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.position = None, None
        self.timer = QTimer(self)
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.on_timeout)

    @pyqtSlot(QJsonValue)
    def print(self, val):
        coords = val.toObject()
        lat, lng = (coords[key].toDouble() for key in ("lat", "lng"))
        self.position = lat, lng
        if not self.timer.isActive():
            self.timer.start()

    def on_timeout(self):
        self.positionChanged.emit(*self.position)


def foo(lat, lng):
    # this function will be called every second.
    print(lat, lng)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    view = QWebEngineView()
    view.setWindowTitle("Serial GPS Emulator");

    backend = Backend(view)
    backend.positionChanged.connect(foo)
    channel = QWebChannel()
    channel.registerObject('backend', backend)
    view.page().setWebChannel(channel)
    view.setHtml(maphtml)
    view.show()
    sys.exit(app.exec_())
于 2018-04-10T20:34:30.227 回答