Panel 0.10.1
Bokek 2.2.3
Plotly 4.8.2 and 4.12.0
Windows 10
Chrome
I hover over Plotly plot. I would not expect exceptions raised but I do.
scoodood has also noted this on Discourse https://discourse.holoviz.org/t/this-panel-plotly-example-is-throwing-error/1363 for the Plotly Reference example.
"""
Scoodood is asking on [Discourse](https://discourse.holoviz.org/t/how-to-capture-the-click-event-on-plotly-plot-with-panel/1360)
How to capture the click event on Plotly plot with Panel?
"""
import numpy as np
from panel.template.react import ReactTemplate
import plotly.graph_objs as go
import panel as pn
pn.extension("plotly")
pn.config.sizing_mode = "stretch_width"
def create_plot():
t = np.linspace(0, 10, 50)
x, y, z = np.cos(t), np.sin(t), t
fig = go.Figure(
data=go.Scatter3d(x=x, y=y, z=z, mode="markers"), layout=dict(title="3D Scatter Plot")
)
fig.layout.autosize = True
return fig
def create_layout(plot):
description_panel = pn.layout.Card(
__doc__, header="# How to capture Plotly Click Events?", sizing_mode="stretch_both"
)
plot_panel = pn.pane.Plotly(plot, config={"responsive": True}, sizing_mode="stretch_both")
settings_panel = plot_panel.controls(jslink=True)
template = ReactTemplate(title="Awesome Panel - Plotly App")
template.sidebar.append(settings_panel)
template.main[0, :] = description_panel
template.main[1:4, :] = plot_panel
return template
def create_app():
plot = create_plot()
return create_layout(plot)
app = create_app()
app.servable()
$ python -m panel serve 'scripts\discourse_1360_plotly_clickevents.py' --dev
2020-10-30 19:25:22,434 Starting Bokeh server version 2.2.3 (running on Tornado 6.0.4)
2020-10-30 19:25:22,436 User authentication hooks NOT provided (default user enabled)
2020-10-30 19:25:22,439 Bokeh app running at: http://localhost:5006/discourse_1360_plotly_clickevents
2020-10-30 19:25:22,440 Starting Bokeh server with process id: 28196
2020-10-30 19:25:25,393 WebSocket connection opened
2020-10-30 19:25:25,394 ServerConnection created
2020-10-30 19:25:30,042 error handling message
message: Message 'PATCH-DOC' content: {'events': [{'kind': 'ModelChanged', 'model': {'id': '1043'}, 'attr': 'hover_data', 'new': ''}], 'references': []}
error: DeserializationError('Dict(String, Any) expected a dict or None, got ')
Traceback (most recent call last):
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\protocol_handler.py",
line 90, in handle
work = await handler(message, connection)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\session.py", line 67,
in _needs_document_lock_wrapper
result = func(self, *args, **kwargs)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\session.py", line 261, in _handle_patch
message.apply_to_document(self.document, self)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\protocol\messages\patch_doc.py", line 100, in apply_to_document
doc._with_self_as_curdoc(lambda: doc.apply_json_patch(self.content, setter))
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 1169, in _with_self_as_curdoc
return f()
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\protocol\messages\patch_doc.py", line 100, in <lambda>
doc._with_self_as_curdoc(lambda: doc.apply_json_patch(self.content, setter))
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 410, in apply_json_patch
patched_obj.set_from_json(attr, value, models=references, setter=setter)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\core\has_props.py", line 345, in set_from_json
descriptor.set_from_json(self, json, models, setter)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\core\property\descriptors.py", line 611, in set_from_json
self.property.from_json(json, models),
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\core\property\container.py",
line 176, in from_json
raise DeserializationError("%s expected a dict or None, got %s" % (self, json))
bokeh.core.property.bases.DeserializationError: Dict(String, Any) expected a dict or None, got
2020-10-30 19:25:30,048 error handling message
message: Message 'PATCH-DOC' content: {'events': [{'kind': 'ModelChanged', 'model': {'id': '1043'}, 'attr': 'hover_data', 'new': ''}], 'references': []}
error: DeserializationError('Dict(String, Any) expected a dict or None, got ')
Traceback (most recent call last):
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\protocol_handler.py",
line 90, in handle
work = await handler(message, connection)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\session.py", line 67,
in _needs_document_lock_wrapper
result = func(self, *args, **kwargs)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\session.py", line 261, in _handle_patch
message.apply_to_document(self.document, self)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\protocol\messages\patch_doc.py", line 100, in apply_to_document
doc._with_self_as_curdoc(lambda: doc.apply_json_patch(self.content, setter))
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 1169, in _with_self_as_curdoc
return f()
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\protocol\messages\patch_doc.py", line 100, in <lambda>
doc._with_self_as_curdoc(lambda: doc.apply_json_patch(self.content, setter))
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 410, in apply_json_patch
patched_obj.set_from_json(attr, value, models=references, setter=setter)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\core\has_props.py", line 345, in set_from_json
descriptor.set_from_json(self, json, models, setter)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\core\property\descriptors.py", line 611, in set_from_json
self.property.from_json(json, models),
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\core\property\container.py",
line 176, in from_json
raise DeserializationError("%s expected a dict or None, got %s" % (self, json))
bokeh.core.property.bases.DeserializationError: Dict(String, Any) expected a dict or None, got
2020-10-30 19:25:32,037 error handling message
message: Message 'PATCH-DOC' content: {'events': [{'kind': 'ModelChanged', 'model': {'id': '1043'}, 'attr': 'hover_data', 'new': ''}], 'references': []}
error: DeserializationError('Dict(String, Any) expected a dict or None, got ')
Traceback (most recent call last):
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\protocol_handler.py",
line 90, in handle
work = await handler(message, connection)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\session.py", line 67,
in _needs_document_lock_wrapper
result = func(self, *args, **kwargs)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\session.py", line 261, in _handle_patch
message.apply_to_document(self.document, self)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\protocol\messages\patch_doc.py", line 100, in apply_to_document
doc._with_self_as_curdoc(lambda: doc.apply_json_patch(self.content, setter))
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 1169, in _with_self_as_curdoc
return f()
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\protocol\messages\patch_doc.py", line 100, in <lambda>
doc._with_self_as_curdoc(lambda: doc.apply_json_patch(self.content, setter))
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 410, in apply_json_patch
patched_obj.set_from_json(attr, value, models=references, setter=setter)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\core\has_props.py", line 345, in set_from_json
descriptor.set_from_json(self, json, models, setter)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\core\property\descriptors.py", line 611, in set_from_json
self.property.from_json(json, models),
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\core\property\container.py",
line 176, in from_json
raise DeserializationError("%s expected a dict or None, got %s" % (self, json))
bokeh.core.property.bases.DeserializationError: Dict(String, Any) expected a dict or None, got
2020-10-30 19:25:32,044 error handling message
message: Message 'PATCH-DOC' content: {'events': [{'kind': 'ModelChanged', 'model': {'id': '1043'}, 'attr': 'hover_data', 'new': ''}], 'references': []}
error: DeserializationError('Dict(String, Any) expected a dict or None, got ')
Traceback (most recent call last):
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\protocol_handler.py",
line 90, in handle
work = await handler(message, connection)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\session.py", line 67,
in _needs_document_lock_wrapper
result = func(self, *args, **kwargs)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\session.py", line 261, in _handle_patch
message.apply_to_document(self.document, self)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\protocol\messages\patch_doc.py", line 100, in apply_to_document
doc._with_self_as_curdoc(lambda: doc.apply_json_patch(self.content, setter))
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 1169, in _with_self_as_curdoc
return f()
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\protocol\messages\patch_doc.py", line 100, in <lambda>
doc._with_self_as_curdoc(lambda: doc.apply_json_patch(self.content, setter))
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 410, in apply_json_patch
patched_obj.set_from_json(attr, value, models=references, setter=setter)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\core\has_props.py", line 345, in set_from_json
descriptor.set_from_json(self, json, models, setter)
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\core\property\descriptors.py", line 611, in set_from_json
self.property.from_json(json, models),
File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\core\property\container.py",
line 176, in from_json
raise DeserializationError("%s expected a dict or None, got %s" % (self, json))
bokeh.core.property.bases.DeserializationError: Dict(String, Any) expected a dict or None, got

hi @MarcSkovMadsen, thanks for bringing this up. This is how it looks in JupyterLab when I was trying to capture the clicked location (x,y,z) on the Plotly chart. I need this (x,y,z) location so that I could drive another chart.
You can see it is throwing error messages when I hover my mouse cursor over the data point. It's very hard to prototype my dashboard with Panel in JupyterLab.
import panel as pn
pn.extension('plotly')
import pandas as pd
import plotly.express as px
data = pd.DataFrame([
('Monday', 7), ('Tuesday', 4), ('Wednesday', 9), ('Thursday', 4),
('Friday', 4), ('Saturday', 4), ('Sunay', 4)], columns=['Day', 'Orders'])
fig = px.line(data, x="Day", y="Orders")
fig.update_traces(mode="lines+markers", marker=dict(size=10), line=dict(width=4))
fig.layout.autosize = True
responsive = pn.pane.Plotly(fig, config={'responsive': True})
pn.Column('# A responsive plot', responsive, sizing_mode='stretch_width')
pn.Row(responsive.controls(jslink=True), responsive)

I have found out that the error is related to "Hover data" giving empty strings, which the Bokeh Dict container can't handle. If I modify the container to set object with length 0 to None, I can get it to work without the DeserializationError. The function I'm changing to get it to work is bokeh/core/property/container.py - Dict.from_json()
def from_json(self, json, models=None):
if json is None or len(json) == 0:
return None
elif isinstance(json, dict):
return { self.keys_type.from_json(key, models): self.values_type.from_json(value, models) for key, value in json.items() }
else:
raise DeserializationError("%s expected a dict or None, got %s" % (self, json))
It also look like Hover data is updating pretty frequently, so I think there is a more underlying problem.

Not sure if it's the proper fix but changing the _process_param_change method of the panel LiteralInput widget by this:
import json
from six import string_types
def _process_param_change(self, msg):
msg = super(pn.widgets.LiteralInput, self)._process_param_change(msg)
if 'value' in msg:
value = '' if msg['value'] is None else msg['value']
if isinstance(value, string_types) and not self.serializer == 'json':
value = repr(value)
elif self.serializer == 'json':
value = json.dumps(value, sort_keys=True) if value != '' else 'null'
else:
value = pn.util.as_unicode(value)
msg['value'] = value
msg['title'] = self.name
return msg
pn.widgets.LiteralInput._process_param_change = _process_param_change
produce a coherent behavior:

However as I'm not sure it's the correct fix I will not submit a PR
Thanks for tracking that down. So this doesn't seem like an issue with the Plotly pane at all then but just the linked widget.
I think there is also another problem with the dict ordering, javascript objects or not key ordered whereas python dict are so event is triggered twice, but I don't know implications of dropping json.dumps sort_key
Most helpful comment
Not sure if it's the proper fix but changing the
_process_param_changemethod of the panelLiteralInputwidget by this:produce a coherent behavior:

However as I'm not sure it's the correct fix I will not submit a PR