import pandas as pd
from dash import callback, get_asset_url, Input, Output, Patch
from pathlib import Path
import plotly.graph_objects as go
from typing import Literal
import vizro.plotly.express as px
from vizro import Vizro
import vizro.models as vm
from vizro.models.types import capture
# Image paths publicly available on Wikimedia Commons
IMG_PATH_DARK = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/38/Iris_versicolor_quebec_1.jpg/320px-Iris_versicolor_quebec_1.jpg"
IMG_PATH_LIGHT = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Iris_virginica_2.jpg/480px-Iris_virginica_2.jpg"
df = px.data.iris()
@callback(
Output("image-chart", "figure", allow_duplicate=True),
# theme-selector is the ID of the Vizro theme toggle button and it's True for light and False for dark
Input("theme-selector", "value"),
prevent_initial_call=True,
)
def update_plot_image_source(theme_value):
path = IMG_PATH_LIGHT if theme_value else IMG_PATH_DARK
# Creating a Patch object
patched_figure = Patch()
patched_figure["layout"]["images"][0]["source"] = path
return patched_figure
@capture("graph")
def plot_image(data_frame, path, width, height, scale_factor):
fig = go.Figure()
# Add invisible scatter trace.
# This trace is added to help the autoresize logic work.
fig.add_trace(
go.Scatter(
x=[0, width * scale_factor],
y=[0, height * scale_factor],
mode="markers",
marker_opacity=0,
showlegend=False,
)
)
# Configure axes
fig.update_xaxes(
visible=False,
range=[0, width * scale_factor]
)
fig.update_yaxes(
visible=False,
range=[0, height * scale_factor],
# the scaleanchor attribute ensures that the aspect ratio stays constant
scaleanchor="x"
)
# Add image
fig.add_layout_image(
dict(
x=0,
sizex=width * scale_factor,
y=height * scale_factor,
sizey=height * scale_factor,
xref="x",
yref="y",
opacity=1.0,
layer="below",
sizing="stretch",
source=path,
)
)
# Configure other layout
fig.update_layout(
width=width * scale_factor,
height=height * scale_factor,
margin={"l": 0, "r": 0, "t": 0, "b": 0},
)
return fig
# Custom component that enables dcc.Graph.config to be configurable
class ConfigurableGraph(vm.Graph):
type: Literal["configurable_graph"] = "configurable_graph"
config: dict = None
def build(self):
# call vm.Graph.build() to get the base graph object
graph_build_obj = super().build()
# get the base graph config
base_graph_config = graph_build_obj[self.id].config
# merge the base graph config with the custom config
graph_build_obj[self.id].config = base_graph_config | self.config or {}
return graph_build_obj
# Enabling custom component to be used within the "vm.Page.components".
vm.Page.add_type("components", ConfigurableGraph)
page = vm.Page(
title="Vizro on PyCafe",
components=[
vm.Graph(
id="image-chart",
figure=plot_image(
data_frame=pd.DataFrame(),
path=IMG_PATH_DARK,
width=1600,
height=900,
scale_factor=0.5,
)
),
vm.Graph(id="scatter_chart", figure=px.scatter(df, x="sepal_length", y="petal_width", color="species")),
ConfigurableGraph(
title="Custom graph with displayModeBar",
figure=px.histogram(df, x="sepal_width", color="species"),
config={"displayModeBar": True},
),
],
controls=[
vm.Filter(column="species"),
],
)
dashboard = vm.Dashboard(pages=[page])
Vizro().build(dashboard).run()