Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: change plotly figure widget to allow image annotation #285

40 changes: 21 additions & 19 deletions solara/components/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,30 +272,32 @@ def FigurePlotly(
on_click: Callable[[Any], None] = None,
on_hover: Callable[[Any], None] = None,
on_unhover: Callable[[Any], None] = None,
on_relayout: Callable[[Any], None] = None,
dependencies=None,
):
from plotly.graph_objs._figurewidget import FigureWidget

def on_points_callback(data):
if data:
event_type = data["event_type"]
if event_type == "plotly_click":
if on_click:
on_click(data)
elif event_type == "plotly_hover":
if on_hover:
on_hover(data)
elif event_type == "plotly_unhover":
if on_unhover:
on_unhover(data)
elif event_type == "plotly_selected":
if on_selection:
on_selection(data)
elif event_type == "plotly_deselect":
if on_deselect:
on_deselect(data)

fig_element = FigureWidget.element(on__js2py_pointsCallback=on_points_callback)
if not data:
return

event_type = data["event_type"]
event_mapping = {
"plotly_click": on_click,
"plotly_hover": on_hover,
"plotly_unhover": on_unhover,
"plotly_selected": on_selection,
"plotly_deselect": on_deselect
}

callback = event_mapping.get(event_type)
if callback:
callback(data)

fig_element = FigureWidget.element(
on__js2py_pointsCallback=on_points_callback,
on__js2py_relayout=on_relayout
)

def update_data():
fig_widget: FigureWidget = solara.get_widget(fig_element)
Expand Down
5 changes: 3 additions & 2 deletions solara/website/pages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from solara.alias import rv
from solara.components.title import Title

from ..components import Header, Hero
#from ..components import Header, Hero


title = "Home"
Expand Down Expand Up @@ -85,6 +85,7 @@ def Layout(children=[]):
solara.Meta(property="og:url", content="https://solara.dev" + router.path)
solara.Meta(property="og:image", content="https://solara.dev/static/assets/images/logo-small.png")
solara.Meta(property="og:type", content="website")
"""
Header(
on_toggle_left_menu=lambda: set_show_left_menu(not show_left_menu),
on_toggle_right_menu=lambda: set_show_right_menu(not show_right_menu),
Expand All @@ -95,7 +96,7 @@ def Layout(children=[]):
sub_title="Solara helps you build powerful & scalable Jupyter and web apps <b>faster</b> and <b>easier</b>.",
button_text="Quickstart",
)

"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this needed? Did you have trouble running this part? Is there something can do about it?
In any case, for this PR I think you want to revert this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maartenbreddels yes, I had issues running the documentation* and ended up removing this files to run the documentation. This wasn't suppose to be deleted, it should be revered now.

*I followed the setup docs but had problems running the tests and docs. I'll try to setup again later and open some issues if needed.

with rv.Container(tag="section", fluid=True, ma_0=True, pa_0=True, class_="fill-height mb-8 solara-content-main"):
if route_current is not None and route_current.path == "/":
description = "Use ipywidgets with Solara to build powerful and scalable web apps for Jupyter and production in Python."
Expand Down
44 changes: 0 additions & 44 deletions solara/website/pages/api/altair.py

This file was deleted.

60 changes: 60 additions & 0 deletions solara/website/pages/examples/visualization/annotator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""# Image annotation with Solara

This example displays how to annotate images with different drawing tools in plotly figures. Use the canvas
below to draw shapes and visualize the canvas callback.


Check [plotly docs](https://dash.plotly.com/annotations) for more information about image annotation.
"""
import json
import plotly.graph_objects as go

import solara

title = "Plotly Image Annotator"
shapes = solara.reactive(None)

class CustomEncoder(json.JSONEncoder):
def default(self, o):
# Convert object instances to their string representation
if isinstance(o, object):
return str(o)
return super().default(o)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? Does plotly give back some objects? If so, it would be good to explain this in this piece of code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does plotly give back some objects?

Yes, I added more explanation about it.


@solara.component
def Page():
def on_relayout(data):
print(data)
if data is None:
return

relayout_data = data['relayout_data']

if "shapes" in relayout_data:
shapes.value = relayout_data["shapes"]

fig = go.FigureWidget(
layout=go.Layout(
showlegend=False,
autosize=False,
width=600,
height=600,
modebar={
"add": [
"drawclosedpath",
"drawcircle",
"drawrect",
"eraseshape",
]
}
)
)

solara.FigurePlotly(fig, on_relayout=on_relayout)

if not shapes.value:
solara.Markdown("## Draw on the canvas")
else:
solara.Markdown("## Data returned by drawing")
formatted_shapes = str(json.dumps(shapes.value, indent=2, cls=CustomEncoder))
solara.Preformatted(formatted_shapes)
44 changes: 0 additions & 44 deletions solara/website/pages/examples/visualization/plotly.py

This file was deleted.

Loading