bokeh / ipywidgets_bokeh

Allows embedding of Jupyter widgets in Bokeh applications.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

panel and ipyleaflet errors (Wrong widgets protocol, Comm is already created)

misolietavec opened this issue · comments

Minimal example

import panel as pn
from ipyleaflet import Map, CircleMarker, MarkerCluster
from ipywidgets import Layout
pn.extension('ipywidgets')

mapcenter = [40.7, -74]
m =  Map(center=mapcenter, layout=Layout(width='400px', height='300px'))
points = []
for k in range(5):
    points.append(CircleMarker(location=[40.7 + 0.01, -74]))
cluster = MarkerCluster(markers=points)
m.add(cluster)

move = pn.widgets.FloatSlider(start=-74., end=-73.95, step=0.01)

@pn.depends(move)
def view_map(move):
    global cluster
    newcent = [40.7, move]
    m.center = newcent
    newmarks = []
    for k in range(5):
        newmarks.append(CircleMarker(location=[40.7 + 0.01, move]))
    cluster.markers = newmarks
    return m

only when executed via panel serve (testing this example in jupyter notebook is OK). After interaction with move slider, the map (initially shown properly) disappears and in development console there are multiple error messages of type:
Wrong widget protocol version: received protocol version '', but was expecting major version '2'
Uncaught (in promise) Error: Comm is already created
(thrown by ipywidgets_bokeh)

This happens with modules versions:
ipyleaflet: 0.18.1, 0.17.4
bokeh: 3.3.3
ipywidgets_bokeh: 1.5.0
panel: 1.3.6, 1.3.1
pyviz_comms: 2.3.2, 3.1.0
(tested in several conda or pip installed virtual environments)

Cannot reproduce with:

ipyleaflet 0.18.1
bokeh 3.3.3
ipywidgets_bokeh 1.5.0
panel 1.3.6

Will have someone else try.

Would also suggest writing this as:

import panel as pn
from ipyleaflet import Map, CircleMarker, MarkerCluster
from ipywidgets import Layout
pn.extension('ipywidgets')

mapcenter = [40.7, -74]
m = Map(center=mapcenter, layout=Layout(width='400px', height='300px'))
points = []
for k in range(5):
    points.append(CircleMarker(location=[40.7 + 0.01, -74]))
cluster = MarkerCluster(markers=points)
m.add(cluster)

move = pn.widgets.FloatSlider(start=-74., end=-73.95, step=0.01)

def view_map(move):
    newcent = [40.7, move.new]
    m.center = newcent
    newmarks = []
    for k in range(5):
        newmarks.append(CircleMarker(location=[40.7 + 0.01, move.new]))
    cluster.markers = newmarks

move.param.watch(view_map, 'value')

pn.Row(move, m).servable()

@philippjfr : Your suggestion works for given minimal example, but I am not sure, how to fix the mentioned problem in case of more complicated situation - I am working with Uber NYC data. For now, will use plotly express scatter_mapbox.

Ah my snippet wasn't intended as a fix, but a more idiomatic way to write the code.

In your snippet, the messages Uncaught (in promise) Error: Comm is already created disappeared, example runs smoothly. Why?

Likely because your code is doing two things:

  1. Updating the Map inplace (m.center = newcent and cluster.markers = newmarks)
  2. Telling Panel to rerender the Map (by using view_map as a reactive callback, this is the problematic part)

I suspect something gets messed up because these two events happen in rapid succession and a race condition might occur where you are updating an ipywidget instance on the frontend that no longer exists. I can only recommend updating your code to avoid doing that.

There is a chance that I can internally catch the condition in Panel and fix it but this approach does remain bad practice.

There's now a fix in Panel that will be released in 1.3.7.

This was dramatic! Many thanks for your activity. I think, I incorporated your changes (although 'by hand') , but this very simple example still throws Uncaught (in promise) Error: Comm is already created . Will wait for panel release.

import panel as pn
import param
from ipyleaflet import Map, CircleMarker
from ipywidgets import Layout
pn.extension('ipywidgets')


class MapView(param.Parameterized):
    move = param.Number(default=-74., bounds=(-74., -73.95), step=0.01)

    @param.depends('move')
    def view_map(self):
        newcent = [40.7, self.move]
        m =  Map(center=newcent, layout=Layout(width='400px', height='300px'))
        m.add(CircleMarker(location=[40.7, self.move]))
        return m


mapview = MapView()
pn.Column(mapview.param, mapview.view_map).servable()

Yeah, could you raise an issue in Panel? That is definitely an issue. I will note though, that that too is very bad practice. Creating a new Map every time the parameter changes will always perform a lot worse than simply updating the Map.

Bad practice, indeed. But it was only to show you that without updating Map inplace the issue is not gone 😄 Will create an issue in Panel soon, with simplified original minimal example and precise environment specification.

Resume I hope. With Map update inplace the small example works as intended. But, what is very gratifying, my NB about NYC taxi now works. I was using pn.widgets.DatePicker outside of parametrized object - the source of all troubles. Thanks to holoviz/panel#921 and https://github.com/MarcSkovMadsen for pointing me to param.CalendarDate. This bigger NB is working in environment where I incorporated your above-mentioned changes. It is not working in other environments, giving the same comm is already created errors. So my big apretiation to you for your intuition and mastery to find well hidden" bugs!