Search code examples
pythonpyqt5qtwebenginefoliumqtwebchannel

folium popups with QtWebChannel


I am displaying a folium generated HTML(for leaflet.js) in a QtWebEngineView. In the popups I have a button w/ an on-click function that's supposed to call a method back in python. But I cant seem to get the channel to work. I'm not sure if I'm doing something wrong with the QtWebChannel or the JS, or could it be folium?

I've injected the following javascript to end of QWebChannel.js, which loads at the end of the <body>

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

    document.getElementById("myBtn").addEventListener("click", function(){
        jshelper.pathSelected("Test!")
    });

Here's my Python Code

import sys
import os

import branca
from branca.element import *

import folium
from folium import plugins

from PyQt5 import QtWebEngineWidgets, QtCore, QtWidgets, QtWebChannel
from PyQt5.QtWidgets import QMainWindow, QAction, QMenu, QApplication, QWidget, QLineEdit, QLabel, QPushButton, QGridLayout, QDockWidget


class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.setObjectName('Main')
        QtCore.QMetaObject.connectSlotsByName(self)

        self.view = QtWebEngineWidgets.QWebEngineView()
        self.view.setObjectName('MapWidget')

        self.window  = QWidget()
        self.window.setObjectName('MainWidget')
        self.layout = QGridLayout()
        self.window.setLayout(self.layout)
        self.layout.addWidget(self.view)
        self.setCentralWidget(self.window);

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

        self.us = folium.Map(location=[36,-108],
                    zoom_start=5, tiles='StamenWatercolor')
        fastestRoute = folium.PolyLine( ((40,-110), (50,-110)),
                    weight=5,color='blue').add_to(self.us)
        f = Figure()
        f.html.add_child(Element('<button id="myBtn">Try it</button>'))
        f.html.add_child(Element('<p>\n TEST \n</p>'))
        link = JavascriptLink('https://rawgit.com/toknowjoyman/qwebch/master/qwebchannel.js')
        f.html.add_child(Element(link.render()))
        print (f.render())
        iframe = branca.element.IFrame(html=f.render(), width=500, height=300)
        popup = folium.Popup(iframe, max_width=500)
        fastestRoute.add_child(popup)
        self.us.save("html/test.html")
        self.view.load(QtCore.QUrl().fromLocalFile(
            os.path.split(os.path.abspath(__file__))[0]+r'/html/test.html'
        ))

        self.setGeometry(100,100,1200,900)
        self.show()

    @QtCore.pyqtSlot(str)
    def pathSelected(self, lat):
      print(lat)

if __name__ == '__main__':
    sys.argv.append("--remote-debugging-port=8000")
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

Would really appreciate the help in figuring this out

Let me know if I should post the folium generated html for the popup or for the leaflet.js api


Solution

  • qwebchannel.js you must put it first, and you are injecting it into the popup and you are registering it in the page().

    To do this we create a Figure () and in the header we add the qwebchannel.js

    principal = Figure()
    js = JavascriptLink(QUrl.fromLocalFile(self.htmlPath.absoluteFilePath("qwebchannel.js")).toString())
    principal.header.add_child(Element(js.render()))
    

    Note: In the qwebchannel does not make the connection with the button because it does not exist.

    for this the popup is passed a new javascript, which will call popup.js, and where I will access jshelper via his parent, the main window.

    popup.js

    var jshelper = parent.jshelper;
    
    document.getElementById("myBtn").addEventListener("click", function(){
        console.log("okay");
        jshelper.pathSelected("Test!");
    });
    

    .py

    f = Figure()
    f.html.add_child(Element('<button id="myBtn">Try it</button>'))
    f.html.add_child(Element('<p>\n TEST \n</p>'))
    
    link = JavascriptLink(QUrl.fromLocalFile(self.htmlPath.absoluteFilePath("popup.js")).toString())
    f.html.add_child(Element(link.render()))
    

    You can find the complete example in the following link.