Skip to content

Commit

Permalink
feat: combine plotly plots into plotly-express plugin (#358)
Browse files Browse the repository at this point in the history
This is probably the most straightforward way to integrate all of plotly
into our plugin. I tested a few plots and they mostly work, but some of
the theming code isn't comprehensive for plots that we don't support
through our plotly express plugin.
One example:
```
import plotly.express as px
df = px.data.tips()

fig = px.density_contour(df, x="total_bill", y="tip", marginal_x="histogram", marginal_y="histogram")
fig.show()
```

<img width="930" alt="Screenshot 2024-03-13 at 10 35 58 AM"
src="https://github.com/deephaven/deephaven-plugins/assets/10480451/9cfc315c-dae4-4933-8ca3-4b3009822812">

These can be easily worked around, but does have to be fixed eventually,
but I'm not sure what the best path is now.
  • Loading branch information
jnumainville authored Apr 3, 2024
1 parent 241348f commit 7a1893d
Show file tree
Hide file tree
Showing 10 changed files with 35 additions and 2 deletions.
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ COPY babel.config.js lerna.json nx.json tsconfig.json ./
# This requires the Dockerfile to be built in the context of the root of the deephaven-plugins repository
# https://stackoverflow.com/a/34300129
COPY plugins plugins
# delete the plotly plugin as it's deprecated
RUN rm -rf plugins/plotly

# Build the JS
RUN npm run build
Expand Down
10 changes: 8 additions & 2 deletions plugins/plotly-express/src/deephaven/plot/express/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Any

from deephaven.plugin.object_type import BidirectionalObjectType, MessageStream
from plotly.graph_objs import Figure

from .communication.DeephavenFigureConnection import DeephavenFigureConnection
from .deephaven_figure import DeephavenFigure
Expand Down Expand Up @@ -66,15 +67,16 @@ def name(self) -> str:

def is_type(self, obj: Any) -> bool:
"""
Check if an object is a DeephavenFigure
Check if an object is a DeephavenFigure or Plotly Figure
Plotly figures are wrapped in DeephavenFigure when sent to the client
Args:
obj: The object to check
Returns:
True if the object is of the correct type, False otherwise
"""
return isinstance(obj, DeephavenFigure)
return isinstance(obj, DeephavenFigure) or isinstance(obj, Figure)

def create_client_connection(
self, obj: DeephavenFigure, connection: MessageStream
Expand All @@ -90,6 +92,10 @@ def create_client_connection(
Returns:
The client connection
"""
if isinstance(obj, Figure):
# this is a plotly figure, it will never be updated, so wrap once and send
obj = DeephavenFigure(obj, is_plotly_fig=True)

figure_connection = DeephavenFigureConnection(obj, connection)
initial_message = json.dumps(
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from plotly import io as pio
from deephaven.plugin import Registration, Callback
from deephaven.plugin.utilities import create_js_plugin, DheSafeCallbackWrapper
from . import DeephavenFigureType
Expand All @@ -24,6 +25,8 @@ def register_into(cls, callback: Callback) -> None:
A function to call after registration
"""
# Disable default renderer to ignore figure.show()
pio.renderers.default = None
callback = DheSafeCallbackWrapper(callback)

callback.register(DeephavenFigureType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ def __init__(
has_color: bool = False,
trace_generator: Generator[dict[str, Any], None, None] | None = None,
has_subplots: bool = False,
is_plotly_fig: bool = False,
):
"""
Create a new DeephavenFigure
Expand All @@ -407,6 +408,7 @@ def __init__(
has_color: If this figure has color
trace_generator: The trace generator
has_subplots: If this figure has subplots
is_plotly_fig: If this is a plotly figure
"""
# keep track of function that called this, and it's args
self._head_node = DeephavenHeadNode()
Expand All @@ -428,6 +430,8 @@ def __init__(

self._has_subplots = has_subplots

self._is_plotly_fig = is_plotly_fig

self._liveness_scope = LivenessScope()

def copy_mappings(self: DeephavenFigure, offset: int = 0) -> list[DataMapping]:
Expand Down Expand Up @@ -584,6 +588,10 @@ def get_figure(self) -> DeephavenFigure | None:
Returns:
The figure
"""
if self._is_plotly_fig:
# a plotly figure was passed directly
# just return this figure since it will never be updated
return self
return self._head_node.get_figure()

def get_plotly_fig(self) -> Figure | None:
Expand Down Expand Up @@ -676,6 +684,7 @@ def copy(self) -> DeephavenFigure:
self._has_color,
self._trace_generator,
self._has_subplots,
self._is_plotly_fig,
)
new_figure._head_node = self._head_node.copy_graph()
return new_figure
Expand Down
4 changes: 4 additions & 0 deletions plugins/plotly/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Deprecated
This plugin is deprecated and will be removed in a future release.
Please use the plotly-express plugin instead, which will render Plotly plots in the same way as this plugin.

# Deephaven Plugin for Plotly

The Deephaven Plugin for Plotly. Allows for opening Plotly plots in a Deephaven environment. Any Plotly plot
Expand Down
3 changes: 3 additions & 0 deletions tests/app.d/express.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from deephaven.column import int_col, string_col
from deephaven import new_table
import deephaven.plot.express as dx
import plotly.express as px

express_source = new_table(
[
Expand All @@ -9,3 +10,5 @@
]
)
express_fig = dx.bar(table=express_source, x="Categories", y="Values")

plotly_fig = px.scatter(x=[0, 1, 2, 3, 4], y=[0, 1, 4, 9, 16])
6 changes: 6 additions & 0 deletions tests/express.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ test('Express loads', async ({ page }) => {
await openPanel(page, 'express_fig', '.js-plotly-plot');
await expect(page.locator('.iris-chart-panel')).toHaveScreenshot();
});

test('Plotly loads', async ({ page }) => {
await gotoPage(page, '');
await openPanel(page, 'plotly_fig', '.js-plotly-plot');
await expect(page.locator('.iris-chart-panel')).toHaveScreenshot();
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7a1893d

Please sign in to comment.