1

I have building a GUI in Python through PyQt5. I am showing a web browser with a google maps page. The user is supposed to move the marker and my program should process the coordinates of the marker. Therefore I have to pass the coordinates from JS to Python, but I can't manage to make it work.

Here is the HTML file:

<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=yes" />
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">

 function geocodePosition(pos) {
  geocoder.geocode({
    latLng: pos
  }, function(responses) {
    if (responses && responses.length > 0) {
      updateMarkerAddress(responses[0].formatted_address);
    } else {
      updateMarkerAddress('Cannot determine address at this location.');
    }
  });
}

function updateMarkerStatus(str) {
  document.getElementById('markerStatus').innerHTML = str;
}

function updateMarkerPosition(latLng) {
  document.getElementById('info').innerHTML = [
    latLng.lat(),
    latLng.lng()
  ].join(', ');
}

function updateMarkerAddress(str) {
  document.getElementById('address').innerHTML = str;
}


var geocoder = new google.maps.Geocoder();
var map;


var goldStar = {
    path: 'M 125,5 155,90 245,90 175,145 200,230 125,180 50,230 75,145 5,90 95,90 z',
    fillColor: 'yellow',
    fillOpacity: 0.8,
    scale: 0.1,
    strokeColor: 'gold',
    strokeWeight: 1
};



function addMarker(lat, lon, city, url) {
    var newmarker = new google.maps.Marker({
        position: new google.maps.LatLng(lat, lon),
        icon: goldStar,
        map: map,
        title: city
    });
    newmarker['infowindow'] = new google.maps.InfoWindow({
            content: url
        });
    google.maps.event.addListener(newmarker, 'click', function() {
        this['infowindow'].open(map, this);
    });
}


function initialize() {
  var latLng = new google.maps.LatLng(40.767367, -111.848007);
  // create as a global variable
  map = new google.maps.Map(document.getElementById('mapCanvas'), {
    zoom: 11,
    center: latLng,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  });
    var marker = new google.maps.Marker({
    position: latLng,
    title: 'Point A',
    map: map,
    draggable: true
  });

  // Update current position info.
  updateMarkerPosition(latLng);
  geocodePosition(latLng);

  // Add dragging event listeners.
  google.maps.event.addListener(marker, 'dragstart', function() {
    updateMarkerAddress('Dragging...');
  });

  google.maps.event.addListener(marker, 'drag', function() {
    updateMarkerStatus('Dragging...');
    updateMarkerPosition(marker.getPosition());
  });

  google.maps.event.addListener(marker, 'dragend', function() {
    updateMarkerStatus('Drag ended');
    geocodePosition(marker.getPosition());
  });

//  return latLng
}


// Onload handler to fire off the app.
google.maps.event.addDomListener(window, 'load', initialize);

</script>
</head>
<body>
  <style>
  #mapCanvas {

    # width: 1000px;
    width: 102%;
    height: 500px;
    float: left;
    margin-left: -7px;
    margin-right: -10px;
    margin-top: -7px;
    margin-bottom: 10px;
  }
  #infoPanel {
    float: center;
    margin-left: 20px;
  }
  #infoPanel div {
    margin-bottom: 10px;
  }
  </style>

      <font size="3" color="black" face="verdana">
  <div id="mapCanvas"></div>
  <div id="infoPanel">
    <font size="3" color="black" face="verdana">
    <!-- <b>Marker status:</b> -->
    <div id="markerStatus"><i>Click and drag the marker.</i></div>
    <font size="3" color="black" face="verdana">
    <b>Current position:</b>
    <div id="info"></div>
    <!--<b>Closest matching address:</b>-->
    <!--<div id="address"></div>-->
  </div>
</body>
</html>

And here is the Python code:

import sys
from PyQt5.QtWidgets import *
from GUI_tmy3 import *

class ShowMap_fun(QMainWindow):
    def __init__(self):
        super().__init__()
        self.map_ui = Ui_tmy3page()  # The name of my top level object is MainWindow
        self.map_ui.setupUi(self)
        self.map_ui.html_code.load(QtCore.QUrl.fromLocalFile('/Users/carlo/Dropbox/modules_NEW/useless.html'))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = ShowMap_fun()
    ex.show()
    sys.exit(app.exec_())

With the GUI code:

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_tmy3page(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(900, 620)
        MainWindow.setMinimumSize(QtCore.QSize(900, 620))
        MainWindow.setMaximumSize(QtCore.QSize(900, 620))
        MainWindow.setWindowTitle("")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.html_code = QtWebEngineWidgets.QWebEngineView(self.centralwidget)
        self.html_code.setGeometry(QtCore.QRect(0, 0, 901, 621))
        self.html_code.setUrl(QtCore.QUrl("about:blank"))
        self.html_code.setObjectName("html_code")
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        pass

from PyQt5 import QtWebEngineWidgets

I understand the easiest method would be to use QWebChannel. I found an example here but I can't adapt it to my case.

Any suggestion?

1 Answer 1

1

To make the work more orderly I have separated the javascript code to a new file called useless.js.

What you should do is create a QWebChannel object, set it on the page and register the object, also you must create a slot that receives the information:

class ShowMap_fun(QMainWindow):
    def __init__(self):
        super().__init__()
        self.map_ui = Ui_tmy3page()  # The name of my top level object is MainWindow
        self.map_ui.setupUi(self)

        channel = QtWebChannel.QWebChannel(self.map_ui.html_code.page())
        self.map_ui.html_code.page().setWebChannel(channel)
        channel.registerObject("jshelper", self)

        self.map_ui.html_code.load(QtCore.QUrl.fromLocalFile(QtCore.QDir.current().filePath("useless.html")))

    @QtCore.pyqtSlot(float, float)
    def markerMoved(self, lat, lng):
        print(lat, lng)

Then you must add the qwebchannel.js file to the .html

useless.html

<html>
<head>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=yes"/>
    <script type="text/javascript" src="./qwebchannel.js"></script>
    <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
    <script type="text/javascript" src="useless.js"></script>
</head>
[...]

In the code js the object must be obtained:

useless.js

var jshelper;

new QWebChannel(qt.webChannelTransport, function (channel) {
    jshelper = channel.objects.jshelper;
});

[...]

google.maps.event.addListener(marker, 'drag', function () {
    updateMarkerStatus('Dragging...');
    updateMarkerPosition(marker.getPosition());
    jshelper.markerMoved(marker.position.lat(), marker.position.lng());
});

The complete example can be found at the following link

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the example @eyllanesc! In a similar way, I've been trying to make a PyQt5.QtCore.pyqtProperty from Python accessible to JS, but it keeps prompting that the property "has no notify signal and is not constant, value updates in HTML will be broken!". Do you have any hint for me on how to properly implement such a property that can be written and read from both Python and JS?
A complete example link is broken. Can you please update it?
Can we use two new QQWebChannel(qt.webChannelTransport, ... in same html?
@eyllanesc Cuz, I wan't to run one Object in setInterval and one Object .onload(). And both use different methods of python.
@Pythoncoder I don't understand you, that callback is only to get the QObject, I think you're confused
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.