KASA
KASA

Reputation: 15

How to pass back value from JS to Python

I wanted to display a 2D Map with Python and then do something with the coordinates of the coursor in the Python code. I cant get the coordinates to the Python Part however. Heres my code:

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()

It would be good if the code would stay in one file but if its totally impossible to do it in one splitting it is acceptable. I guess the first step in solving mny Problem would be to call a function from the HTML/JS part as backend.print("test") also doesnt work. I also noticed that self.exec() Blocks the rest of the code, is there a way to execute any other code while the map is running? Thanks!

Upvotes: 1

Views: 1310

Answers (1)

eyllanesc
eyllanesc

Reputation: 243955

I do not see where in your code you have passed the backend.

In this case you must create a Backend class that can be injected, and for a method to be invoked it must be a slot, for this you must use the pyqtSlot() setting, the parameter that it receives depends on what you are sending it, in the case of the e.latlng is a QJsonValue. In the slot you must separate the necessary parts.

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_())

Upvotes: 2

Related Questions