Skip to content

PlotlyMulti

py
#!/usr/bin/env -S uv run --script
# -----------------------------------------------------------------------------
# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "pandas",
#     "plotly",
#     "trame-plotly",
#     "trame-vuetify",
#     "trame[app]",
# ]
# ///
# -----------------------------------------------------------------------------
"""
Trame Plotly Chart Selector

Running with uv
   uv run ./00_plotly_charts_selector.py
   or ./00_plotly_charts_selector.py

Required Packages:
   pip install "trame[app]" trame-vuetify trame-plotly plotly pandas jupyterlab

Run as a Desktop Application:
   python 00_plotly_charts_selector.py --app

Run in Jupyter Lab / Notebook:
   Make sure this script ('00_plotly_charts_selector.py') is in the same directory as your notebook,
   or in a directory included in Python's path.
   Then, in a cell, execute:

   from 00_plotly_charts_selector import PlotlyViewer
   PlotlyViewer()

Run as a Web Application (default):
   python 00_plotly_charts_selector.py --server
   # Access via the URLs provided in the console (e.g., http://localhost:8080)
"""

from trame.app import TrameApp
from trame.decorators import change
from trame.ui.vuetify3 import SinglePageLayout
from trame.widgets import vuetify3, plotly

import plotly.express as px
import plotly.graph_objects as go

# -----------------------------------------------------------------------------
# Sample Plotly figures
# -----------------------------------------------------------------------------
SCATTER_MATRIX = px.scatter_matrix(
    px.data.iris(),
    dimensions=["sepal_width", "sepal_length", "petal_width", "petal_length"],
    color="species",
)
SCATTER_3D = px.scatter_3d(
    px.data.iris(),
    x="sepal_length",
    y="sepal_width",
    z="petal_width",
    color="petal_length",
    symbol="species",
)
BAR_CHART = go.Figure(
    data=[go.Bar(x=[1, 2, 3], y=[1, 3, 2])], layout_title_text="A Bar Chart"
)
CONTOUR_PLOT = go.Figure(
    data=[
        go.Contour(
            z=[
                [10, 10.625, 12.5, 15.625, 20],
                [5.625, 6.25, 8.125, 11.25, 15.625],
                [2.5, 3.125, 5.0, 8.125, 12.5],
                [0.625, 1.25, 3.125, 6.25, 10.625],
                [0, 0.625, 2.5, 5.625, 10],
            ]
        )
    ]
)
CONTOUR_PLOT.update_layout(title_text="Contour Plot")

PLOTS = {
    "Contour": CONTOUR_PLOT,
    "Scatter3D": SCATTER_3D,
    "ScatterMatrix": SCATTER_MATRIX,
    "BarChart": BAR_CHART,
}

# -----------------------------------------------------------------------------
# Trame Application
# -----------------------------------------------------------------------------


class PlotlyViewer(TrameApp):
    def __init__(self, server=None):
        super().__init__(server, client_type="vue3")
        self._build_ui()

    @change("active_plot_name")
    def update_plot_figure(self, active_plot_name, **_):
        figure_data = PLOTS.get(active_plot_name)
        if figure_data:
            self.ctx.plotly_display.update(figure_data)

    def _build_ui(self):
        with SinglePageLayout(self.server, full_height=True) as self.ui:
            self.ui.title.set_text("Trame ❤️ Plotly")
            self.ui.icon.hide()

            with self.ui.toolbar:
                vuetify3.VSpacer()
                vuetify3.VSelect(
                    v_model=("active_plot_name", "Contour"),
                    items=("plots", list(PLOTS.keys())),
                    hide_details=True,
                    density="compact",
                    style="max-width: 200px;",
                    variant="outlined",
                    classes="mr-4",
                )

            with self.ui.content:
                plotly.Figure(
                    ctx_name="plotly_display",
                    display_logo=False,
                    display_mode_bar=True,
                )


# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------


def main(**kwargs):
    app = PlotlyViewer()  # Instantiate the class, passing the server
    app.server.start(**kwargs)  # Start the specific server instance


if __name__ == "__main__":
    main()
#!/usr/bin/env -S uv run --script
# -----------------------------------------------------------------------------
# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "pandas",
#     "plotly",
#     "trame-plotly",
#     "trame-vuetify",
#     "trame[app]",
# ]
# ///
# -----------------------------------------------------------------------------
"""
Trame Plotly Chart Selector

Running with uv
   uv run ./00_plotly_charts_selector.py
   or ./00_plotly_charts_selector.py

Required Packages:
   pip install "trame[app]" trame-vuetify trame-plotly plotly pandas jupyterlab

Run as a Desktop Application:
   python 00_plotly_charts_selector.py --app

Run in Jupyter Lab / Notebook:
   Make sure this script ('00_plotly_charts_selector.py') is in the same directory as your notebook,
   or in a directory included in Python's path.
   Then, in a cell, execute:

   from 00_plotly_charts_selector import PlotlyViewer
   PlotlyViewer()

Run as a Web Application (default):
   python 00_plotly_charts_selector.py --server
   # Access via the URLs provided in the console (e.g., http://localhost:8080)
"""

from trame.app import TrameApp
from trame.decorators import change
from trame.ui.vuetify3 import SinglePageLayout
from trame.widgets import vuetify3, plotly

import plotly.express as px
import plotly.graph_objects as go

# -----------------------------------------------------------------------------
# Sample Plotly figures
# -----------------------------------------------------------------------------
SCATTER_MATRIX = px.scatter_matrix(
    px.data.iris(),
    dimensions=["sepal_width", "sepal_length", "petal_width", "petal_length"],
    color="species",
)
SCATTER_3D = px.scatter_3d(
    px.data.iris(),
    x="sepal_length",
    y="sepal_width",
    z="petal_width",
    color="petal_length",
    symbol="species",
)
BAR_CHART = go.Figure(
    data=[go.Bar(x=[1, 2, 3], y=[1, 3, 2])], layout_title_text="A Bar Chart"
)
CONTOUR_PLOT = go.Figure(
    data=[
        go.Contour(
            z=[
                [10, 10.625, 12.5, 15.625, 20],
                [5.625, 6.25, 8.125, 11.25, 15.625],
                [2.5, 3.125, 5.0, 8.125, 12.5],
                [0.625, 1.25, 3.125, 6.25, 10.625],
                [0, 0.625, 2.5, 5.625, 10],
            ]
        )
    ]
)
CONTOUR_PLOT.update_layout(title_text="Contour Plot")

PLOTS = {
    "Contour": CONTOUR_PLOT,
    "Scatter3D": SCATTER_3D,
    "ScatterMatrix": SCATTER_MATRIX,
    "BarChart": BAR_CHART,
}

# -----------------------------------------------------------------------------
# Trame Application
# -----------------------------------------------------------------------------


class PlotlyViewer(TrameApp):
    def __init__(self, server=None):
        super().__init__(server, client_type="vue3")
        self._build_ui()

    @change("active_plot_name")
    def update_plot_figure(self, active_plot_name, **_):
        figure_data = PLOTS.get(active_plot_name)
        if figure_data:
            self.ctx.plotly_display.update(figure_data)

    def _build_ui(self):
        with SinglePageLayout(self.server, full_height=True) as self.ui:
            self.ui.title.set_text("Trame ❤️ Plotly")
            self.ui.icon.hide()

            with self.ui.toolbar:
                vuetify3.VSpacer()
                vuetify3.VSelect(
                    v_model=("active_plot_name", "Contour"),
                    items=("plots", list(PLOTS.keys())),
                    hide_details=True,
                    density="compact",
                    style="max-width: 200px;",
                    variant="outlined",
                    classes="mr-4",
                )

            with self.ui.content:
                plotly.Figure(
                    ctx_name="plotly_display",
                    display_logo=False,
                    display_mode_bar=True,
                )


# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------


def main(**kwargs):
    app = PlotlyViewer()  # Instantiate the class, passing the server
    app.server.start(**kwargs)  # Start the specific server instance


if __name__ == "__main__":
    main()
py
r"""
Installation requirements:
    pip install trame trame-vuetify trame-components trame-plotly
"""

import numpy as np
import pandas as pd
import plotly.figure_factory as ff
import plotly.graph_objects as go

from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import plotly, trame, vuetify

# -----------------------------------------------------------------------------
# Trame setup
# -----------------------------------------------------------------------------

server = get_server(client_type="vue2")
state, ctrl = server.state, server.controller

# -----------------------------------------------------------------------------
# Charts handling
# -----------------------------------------------------------------------------

contour_raw_data = pd.read_json(
    "https://raw.githubusercontent.com/plotly/datasets/master/contour_data.json"
)
scatter_raw_data = pd.read_json(
    "https://raw.githubusercontent.com/plotly/datasets/master/scatter_data.json"
)
polar_data = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/polar_dataset.csv"
)

scatter_data = scatter_raw_data["Data"]


def clean_data(data_in):
    """
    Cleans data in a format which can be conveniently
    used for drawing traces. Takes a dictionary as the
    input, and returns a list in the following format:

    input = {'key': ['a b c']}
    output = [key, [a, b, c]]
    """
    key = list(data_in.keys())[0]
    data_out = [key]
    for i in data_in[key]:
        data_out.append(list(map(float, i.split(" "))))

    return data_out


def create_ternary_fig(width=300, height=300, **kwargs):
    contour_dict = contour_raw_data["Data"]

    # Defining a colormap:
    colors = [
        "#8dd3c7",
        "#ffffb3",
        "#bebada",
        "#fb8072",
        "#80b1d3",
        "#fdb462",
        "#b3de69",
        "#fccde5",
        "#d9d9d9",
        "#bc80bd",
    ]
    colors_iterator = iter(colors)

    fig = go.Figure()

    for raw_data in contour_dict:
        data = clean_data(raw_data)

        a = [inner_data[0] for inner_data in data[1:]]
        a.append(data[1][0])  # Closing the loop

        b = [inner_data[1] for inner_data in data[1:]]
        b.append(data[1][1])  # Closing the loop

        c = [inner_data[2] for inner_data in data[1:]]
        c.append(data[1][2])  # Closing the loop

        fig.add_trace(
            go.Scatterternary(
                text=data[0],
                a=a,
                b=b,
                c=c,
                mode="lines",
                line=dict(color="#444", shape="spline"),
                fill="toself",
                fillcolor=colors_iterator.__next__(),
            )
        )

    fig.update_layout(
        margin=dict(l=50, r=50, t=50, b=50),
        width=width,
        height=height,
    )
    return fig


def create_polar_fig(width=300, height=300, **kwargs):
    fig = go.Figure()
    fig.add_trace(
        go.Scatterpolar(
            r=polar_data["x1"].tolist(),
            theta=polar_data["y"].tolist(),
            mode="lines",
            name="Figure 8",
            line_color="peru",
        )
    )
    fig.add_trace(
        go.Scatterpolar(
            r=polar_data["x2"].tolist(),
            theta=polar_data["y"].tolist(),
            mode="lines",
            name="Cardioid",
            line_color="darkviolet",
        )
    )
    fig.add_trace(
        go.Scatterpolar(
            r=polar_data["x3"].tolist(),
            theta=polar_data["y"].tolist(),
            mode="lines",
            name="Hypercardioid",
            line_color="deepskyblue",
        )
    )

    fig.update_layout(
        # title = 'Mic Patterns',
        margin=dict(l=20, r=20, t=20, b=20),
        showlegend=False,
        width=width,
        height=height,
    )
    return fig


def create_streamline_fig(width=100, height=100, **kwargs):
    x = np.linspace(-3, 3, 100)
    y = np.linspace(-3, 3, 100)
    Y, X = np.meshgrid(x, y)
    u = -1 - X**2 + Y
    v = 1 + X - Y**2

    # Create streamline figure
    fig = ff.create_streamline(
        x.tolist(),
        y.tolist(),
        u.tolist(),
        v.tolist(),
        arrow_scale=0.1,
    )
    fig.update_layout(
        # title = 'Mic Patterns',
        margin=dict(l=20, r=20, t=10, b=10),
        showlegend=False,
        width=width,
        height=height,
    )
    return fig


def create_contour_fig(width=100, height=100, **kwargs):
    fig = go.Figure(
        data=go.Contour(
            z=[
                [2, 4, 7, 12, 13, 14, 15, 16],
                [3, 1, 6, 11, 12, 13, 16, 17],
                [4, 2, 7, 7, 11, 14, 17, 18],
                [5, 3, 8, 8, 13, 15, 18, 19],
                [7, 4, 10, 9, 16, 18, 20, 19],
                [9, 10, 5, 27, 23, 21, 21, 21],
                [11, 14, 17, 26, 25, 24, 23, 22],
            ],
            contours={
                "coloring": "heatmap",
                "showlabels": True,
                "labelfont": {
                    "size": 12,
                    "color": "black",
                },
            },
            line_smoothing=1,
        )
    )
    fig.update_layout(
        # title = 'Mic Patterns',
        margin=dict(l=20, r=20, t=10, b=10),
        showlegend=False,
        width=width,
        height=height,
    )
    return fig


# -----------------------------------------------------------------------------
# Callbacks
# -----------------------------------------------------------------------------


@state.change("contour_size")
def update_contour_size(contour_size, **kwargs):
    if contour_size is None:
        return

    ctrl.update_contour(create_contour_fig(**contour_size.get("size")))


@state.change("stream_size")
def update_stream_size(stream_size, **kwargs):
    if stream_size is None:
        return

    ctrl.update_stream(create_streamline_fig(**stream_size.get("size")))


@state.change("polar_size")
def update_polar_size(polar_size, **kwargs):
    if polar_size is None:
        return

    ctrl.update_polar(create_polar_fig(**polar_size.get("size")))


@state.change("ternary_size")
def update_ternary_size(ternary_size, **kwargs):
    if ternary_size is None:
        return

    ctrl.update_ternary(create_ternary_fig(**ternary_size.get("size")))


# -----------------------------------------------------------------------------
# GUI
# -----------------------------------------------------------------------------


state.trame__title = "Charts"

with SinglePageLayout(server) as layout:
    layout.title.set_text("Many charts")

    with layout.content:
        with vuetify.VContainer(fluid=True, classes="fill-height"):
            with vuetify.VRow(style="height: 50%;"):
                with vuetify.VCol():
                    with trame.SizeObserver("polar_size"):
                        ctrl.update_polar = plotly.Figure(
                            display_mode_bar=("false",),
                        ).update
                with vuetify.VCol():
                    with trame.SizeObserver("ternary_size"):
                        ctrl.update_ternary = plotly.Figure(
                            display_mode_bar=("false",),
                        ).update
            with vuetify.VRow(style="height: 50%;"):
                with vuetify.VCol():
                    with trame.SizeObserver("contour_size"):
                        ctrl.update_contour = plotly.Figure(
                            display_mode_bar=("false",),
                        ).update
                with vuetify.VCol():
                    with trame.SizeObserver("stream_size"):
                        ctrl.update_stream = plotly.Figure(
                            display_mode_bar=("false",),
                        ).update

# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------

if __name__ == "__main__":
    server.start()
r"""
Installation requirements:
    pip install trame trame-vuetify trame-components trame-plotly
"""

import numpy as np
import pandas as pd
import plotly.figure_factory as ff
import plotly.graph_objects as go

from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import plotly, trame, vuetify

# -----------------------------------------------------------------------------
# Trame setup
# -----------------------------------------------------------------------------

server = get_server(client_type="vue2")
state, ctrl = server.state, server.controller

# -----------------------------------------------------------------------------
# Charts handling
# -----------------------------------------------------------------------------

contour_raw_data = pd.read_json(
    "https://raw.githubusercontent.com/plotly/datasets/master/contour_data.json"
)
scatter_raw_data = pd.read_json(
    "https://raw.githubusercontent.com/plotly/datasets/master/scatter_data.json"
)
polar_data = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/polar_dataset.csv"
)

scatter_data = scatter_raw_data["Data"]


def clean_data(data_in):
    """
    Cleans data in a format which can be conveniently
    used for drawing traces. Takes a dictionary as the
    input, and returns a list in the following format:

    input = {'key': ['a b c']}
    output = [key, [a, b, c]]
    """
    key = list(data_in.keys())[0]
    data_out = [key]
    for i in data_in[key]:
        data_out.append(list(map(float, i.split(" "))))

    return data_out


def create_ternary_fig(width=300, height=300, **kwargs):
    contour_dict = contour_raw_data["Data"]

    # Defining a colormap:
    colors = [
        "#8dd3c7",
        "#ffffb3",
        "#bebada",
        "#fb8072",
        "#80b1d3",
        "#fdb462",
        "#b3de69",
        "#fccde5",
        "#d9d9d9",
        "#bc80bd",
    ]
    colors_iterator = iter(colors)

    fig = go.Figure()

    for raw_data in contour_dict:
        data = clean_data(raw_data)

        a = [inner_data[0] for inner_data in data[1:]]
        a.append(data[1][0])  # Closing the loop

        b = [inner_data[1] for inner_data in data[1:]]
        b.append(data[1][1])  # Closing the loop

        c = [inner_data[2] for inner_data in data[1:]]
        c.append(data[1][2])  # Closing the loop

        fig.add_trace(
            go.Scatterternary(
                text=data[0],
                a=a,
                b=b,
                c=c,
                mode="lines",
                line=dict(color="#444", shape="spline"),
                fill="toself",
                fillcolor=colors_iterator.__next__(),
            )
        )

    fig.update_layout(
        margin=dict(l=50, r=50, t=50, b=50),
        width=width,
        height=height,
    )
    return fig


def create_polar_fig(width=300, height=300, **kwargs):
    fig = go.Figure()
    fig.add_trace(
        go.Scatterpolar(
            r=polar_data["x1"].tolist(),
            theta=polar_data["y"].tolist(),
            mode="lines",
            name="Figure 8",
            line_color="peru",
        )
    )
    fig.add_trace(
        go.Scatterpolar(
            r=polar_data["x2"].tolist(),
            theta=polar_data["y"].tolist(),
            mode="lines",
            name="Cardioid",
            line_color="darkviolet",
        )
    )
    fig.add_trace(
        go.Scatterpolar(
            r=polar_data["x3"].tolist(),
            theta=polar_data["y"].tolist(),
            mode="lines",
            name="Hypercardioid",
            line_color="deepskyblue",
        )
    )

    fig.update_layout(
        # title = 'Mic Patterns',
        margin=dict(l=20, r=20, t=20, b=20),
        showlegend=False,
        width=width,
        height=height,
    )
    return fig


def create_streamline_fig(width=100, height=100, **kwargs):
    x = np.linspace(-3, 3, 100)
    y = np.linspace(-3, 3, 100)
    Y, X = np.meshgrid(x, y)
    u = -1 - X**2 + Y
    v = 1 + X - Y**2

    # Create streamline figure
    fig = ff.create_streamline(
        x.tolist(),
        y.tolist(),
        u.tolist(),
        v.tolist(),
        arrow_scale=0.1,
    )
    fig.update_layout(
        # title = 'Mic Patterns',
        margin=dict(l=20, r=20, t=10, b=10),
        showlegend=False,
        width=width,
        height=height,
    )
    return fig


def create_contour_fig(width=100, height=100, **kwargs):
    fig = go.Figure(
        data=go.Contour(
            z=[
                [2, 4, 7, 12, 13, 14, 15, 16],
                [3, 1, 6, 11, 12, 13, 16, 17],
                [4, 2, 7, 7, 11, 14, 17, 18],
                [5, 3, 8, 8, 13, 15, 18, 19],
                [7, 4, 10, 9, 16, 18, 20, 19],
                [9, 10, 5, 27, 23, 21, 21, 21],
                [11, 14, 17, 26, 25, 24, 23, 22],
            ],
            contours={
                "coloring": "heatmap",
                "showlabels": True,
                "labelfont": {
                    "size": 12,
                    "color": "black",
                },
            },
            line_smoothing=1,
        )
    )
    fig.update_layout(
        # title = 'Mic Patterns',
        margin=dict(l=20, r=20, t=10, b=10),
        showlegend=False,
        width=width,
        height=height,
    )
    return fig


# -----------------------------------------------------------------------------
# Callbacks
# -----------------------------------------------------------------------------


@state.change("contour_size")
def update_contour_size(contour_size, **kwargs):
    if contour_size is None:
        return

    ctrl.update_contour(create_contour_fig(**contour_size.get("size")))


@state.change("stream_size")
def update_stream_size(stream_size, **kwargs):
    if stream_size is None:
        return

    ctrl.update_stream(create_streamline_fig(**stream_size.get("size")))


@state.change("polar_size")
def update_polar_size(polar_size, **kwargs):
    if polar_size is None:
        return

    ctrl.update_polar(create_polar_fig(**polar_size.get("size")))


@state.change("ternary_size")
def update_ternary_size(ternary_size, **kwargs):
    if ternary_size is None:
        return

    ctrl.update_ternary(create_ternary_fig(**ternary_size.get("size")))


# -----------------------------------------------------------------------------
# GUI
# -----------------------------------------------------------------------------


state.trame__title = "Charts"

with SinglePageLayout(server) as layout:
    layout.title.set_text("Many charts")

    with layout.content:
        with vuetify.VContainer(fluid=True, classes="fill-height"):
            with vuetify.VRow(style="height: 50%;"):
                with vuetify.VCol():
                    with trame.SizeObserver("polar_size"):
                        ctrl.update_polar = plotly.Figure(
                            display_mode_bar=("false",),
                        ).update
                with vuetify.VCol():
                    with trame.SizeObserver("ternary_size"):
                        ctrl.update_ternary = plotly.Figure(
                            display_mode_bar=("false",),
                        ).update
            with vuetify.VRow(style="height: 50%;"):
                with vuetify.VCol():
                    with trame.SizeObserver("contour_size"):
                        ctrl.update_contour = plotly.Figure(
                            display_mode_bar=("false",),
                        ).update
                with vuetify.VCol():
                    with trame.SizeObserver("stream_size"):
                        ctrl.update_stream = plotly.Figure(
                            display_mode_bar=("false",),
                        ).update

# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------

if __name__ == "__main__":
    server.start()