JaWeilBaum / pyqtlet2

pyqtlet2 extends pyqtlet which initially brought Leaflet maps to PyQt5 and PySide6.
Other
37 stars 19 forks source link

Map container is already initialized. #24

Open EasyIsrael opened 2 years ago

EasyIsrael commented 2 years ago

Hi, I'm trying to use your very nice library in a Jupyter Notebook.

But currently i'm stumbling over the problem, that the Map is already loaded, when i want to load the Jupyter Notebook Cell again.

Here is my Testcode:

from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QCoreApplication
from pyqtlet2 import L, MapWidget

class MapWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.mapWidget = MapWidget()
        self.setCentralWidget(self.mapWidget)
        self.map = L.map(self.mapWidget)
        self.map.setView([12.97, 77.59], 10)
        L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(self.map)
        self.show()

app = QCoreApplication.instance()
if app is None: app = QApplication([])

window=MapWindow()
window.show()
app.exec_()   

This error is known in the leaflet comunity:

and there are different solutions for that. For example:

if (map != undefined) { map.remove(); } 

But i currently don't know where to put this in the library code.

JaWeilBaum commented 2 years ago

Hi @EasyIsrael, thanks for reaching out. First of, I never tried using PyQt5 in a Jupyter notebook. Indeed your code seems not to work. I tested the demo script which is provided in the README.md and this seems to work fine, as long as you don't want to reload the cell.

Two more questions from my side:

EasyIsrael commented 2 years ago

i'm testing my code always as a snippet in a jupyter notebook. It's my way for a faster development. It's not that important that it works in a notebook, but that shows me, that there is something wrong with the initialisation. it seems to be the same problem as already mentioned here, that you can not use multiple maps in one Application and that you are not able to reload the map.

So when one wants to reload the whole site, then he needs to put something like map.remove() in it.

I think it is a bigger task to solve the multi map problem, but the reload problem could be easily solved by a map.remove() at the right position. But i'm not so familiar with this mixed python/javascript programming. that's why i was asking.

JaWeilBaum commented 2 years ago

Thanks for the explenation - makes sense to use a jupyter notebook for this!

I've seen the issues in the origignal project which relate to the multi map issue. In regards to the reloading issue, the Reload menu was removed by the following PR https://github.com/JaWeilBaum/pyqtlet2/pull/19 and should be gone from version 0.5.1 onwards.

I'm still not 100% sure if I get the problem in regards to the reload mechanism. You want to reload teh map? When I run the snippet you sent from the console everything works as expected. If you are looking into a mechanism on how to reload the QWebPage while the application is running, I guess I can have a look into it.

EasyIsrael commented 2 years ago

To test it you have to load the cell again after closing the qtwindow from the first execution. The first time it works perfectly.

Beginning from the second execution it stops working.

The Javascript code remains somehow in the memory. And when you execute the code again. The new Javascript code will be added to the page again.

Leon Friedmann @.***> schrieb am So., 20. Feb. 2022, 16:25:

Thanks for the explenation - makes sense to use a jupyter notebook for this!

I've seen the issues in the origignal project which relate to the multi map issue. In regards to the reloading issue, the Reload menu was removed by the following PR #19 https://github.com/JaWeilBaum/pyqtlet2/pull/19 and should be gone from version 0.5.1 onwards.

I'm still not 100% sure if I get the problem in regards to the reload mechanism. You want to reload teh map? When I run the snippet you sent from the console everything works as expected. If you are looking into a mechanism on how to reload the QWebPage while the application is running, I guess I can have a look into it.

— Reply to this email directly, view it on GitHub https://github.com/JaWeilBaum/pyqtlet2/issues/24#issuecomment-1046261692, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXHBXN5A6T27A6XDWBRPBDU4EBXBANCNFSM5OYDHH5A . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you were mentioned.Message ID: @.***>

JaWeilBaum commented 2 years ago

Now I got the issue and I'm able to reproduce it - sorry that this took so long.

I checked the map.remove(); call. You can go ahead and add this to map.py if you want to test around.

def removeMap(self):
    js = 'map.remove();'
    self.runJavaScript(js)

During further debugging I also connected to the unload() event. According to the leaflet documentation:

Fired when the map is destroyed with remove method.

Interestingly this event is first called when you execute the jupyter cell again, and not directly after the map.remove(); call. Which leads me to the lifecycle of PyQt when beeing terminated and restarted by a juypter cell. With a quick google serach I was not able to find much information about the lifecyle information of PyQt in this case.

My current assumption is that the map-widget it self is not correctly deinitialized, which leads to this issues. If I would have deeper understanding on the application lifecyle (after closing the window) I might be able to correctly deinit the Widget so that a new initialization would work again.

EasyIsrael commented 2 years ago

Hi Leon,

okay, we are one step further.

Javascript is not claiming anymore if you append the removeMap. But executing the next time leads to a blank screen.

This is my code:

from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QCoreApplication
from pyqtlet2 import L, MapWidget

class MapWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.mapWidget = MapWidget()
        self.setCentralWidget(self.mapWidget)
        self.map = L.map(self.mapWidget)
        self.map.setView([12.97, 77.59], 10)
        L.tileLayer('http://{s}.
tile.osm.org/{z}/{x}/{y}.png').addTo(self.map)
        self.show()

    def closeEvent(self, e=None):
        js = 'map.remove();'
        self.map.runJavaScript(js)
        print("close application")

On Sun, Feb 20, 2022 at 11:03 PM Leon Friedmann @.***> wrote:

Now I got the issue and I'm able to reproduce it - sorry that this took so long.

I checked the map.remove(); call. You can go ahead and add this to map.py if you want to test around.

def removeMap(self): js = 'map.remove();' self.runJavaScript(js)

During further debugging I also connected to the unload() event. According to the leaflet documentation:

Fired when the map is destroyed with remove https://leafletjs.com/reference.html#map-remove method.

Interestingly this event is first called when you execute the jupyter cell again, and not directly after the map.remove(); call. Which leads me to the lifecycle of PyQt when beeing terminated and restarted by a juypter cell. With a quick google serach I was not able to find much information about the lifecyle information of PyQt in this case.

My current assumption is that the map-widget it self is not correctly deinitialized, which leads to this issues. If I would have deeper understanding on the application lifecyle (after closing the window) I might be able to correctly deinit the Widget so that a new initialization would work again.

— Reply to this email directly, view it on GitHub https://github.com/JaWeilBaum/pyqtlet2/issues/24#issuecomment-1046330109, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXHBXLXOQPVYFJSECQH7W3U4FQM5ANCNFSM5OYDHH5A . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you were mentioned.Message ID: @.***>

EasyIsrael commented 2 years ago

I don't know if that has something to do with this problem, but i'll write it here.

It seems that our map container is not initialized correct. if i'm right, we all get this message:

js: Uncaught TypeError: Cannot read property 'mapObject' of null

it seems that this is due to an too early binding. I found on a different implementation of leaflet this: https://vue2-leaflet.netlify.app/faq/#how-can-i-access-the-leaflet-map-object

data: () => ({map: null}), mounted () { // DON'T this.map = this.$refs.map.mapObject // doesn't work, this.map is null

// DO this.$nextTick(() => { this.map = this.$refs.map.mapObject // work as expected }) },

They do the map assignment after the first Tick(). Maybe we should consider doing something similar.

when doing a later map assignment from the python side it has no effect (i tested QTimer). this seems to be something inside pyqtlet2 during the instantiation process.

JaWeilBaum commented 2 years ago

Seems to be interesting! I did not went this deep into the code at this specific part.

Did you look into this further or not?

EasyIsrael commented 2 years ago

not yet. currently i'm okay with how it works now. But it is not clean. Someday we should look deeper into it.