How to create a semi-pie chart with percentage?

Is it possible to create something like below with the holoviz libraries?
image

1 Like

I don’t think so. I would really like something like this as well.

There is a Feature Request here https://github.com/holoviz/holoviews/issues/4363. I’ve added your request there.

Feel free to like it to get it higher on the priority list.

2 Likes

But you can use Plotly in Panel and Plotly provides these things here https://plotly.com/python/indicator/

And if you don’t mind using panel.pane.HTML you can use html and css to create it. See https://codepen.io/vineethtrv/pen/xGjQOX

1 Like

Thanks again for your insightful solutions!

1 Like

If you get something working and can share a screenshot and/ or code, then please share. Would be awesome to see. Thanks.

Hello !!! I tried to use a plotly indicator to indicate some properties in my panel. I update the plotly figure with pn.depends but the new figure overlaps with the old figure. You can see the gif

I do similar things with matplotlib figure and the old figure disappears with the same approach. The MRE is the next one:


import time 
import panel as pn
pn.extension("plotly")

import plotly.graph_objects as go

fig = go.Figure()

slider = pn.widgets.IntSlider(name='silu', start =0, end = 1000, value = 0, width = 200)

@pn.depends(slider.param.value)
def fip(e):
    fig.add_trace(go.Indicator(
                    value = 20*slider.value,
                    delta = {'reference': 160},
                    gauge = { 'axis': {'visible': False} },
                    domain = {'row': 0, 'column': 0}
                            )
                )
       
    fig.update_layout(
    grid = {'rows': 1, 'columns': 1, 'pattern': "independent"},
    template = {'data' : {'indicator': [{
        'title': {'text': "Speed"},
        'mode' : "number+delta+gauge",
        'delta' : {'reference': 90}}]
                         }})   
    return fig

def b(e):
    for i  in range (10):
        slider.value += 10
        print ('value changed')
        time.sleep(1)

        
btn = pn.widgets.Button(name = 'button', width =200)

btn.on_click(b)

pn.Row(pn.Column(btn, slider), fip).show()

I tried to run this code with .show in the notebook and bith bokeh serve in the terminal, but the behaviour is the same.

Thanks for the help,
N

Hi @nghenzi2019

You are almost there. The problem is that you append more and more traces. You need to clear the data of the figure each time you run fip: fig.data = []

import time
import panel as pn
pn.extension("plotly")

import plotly.graph_objects as go

fig = go.Figure()

slider = pn.widgets.IntSlider(name='silu', start =0, end = 1000, value = 0, width = 200)

@pn.depends(slider.param.value)
def fip(e):
    fig.data = []
    fig.add_trace(go.Indicator(
                    value = 20*slider.value,
                    delta = {'reference': 160},
                    gauge = { 'axis': {'visible': False} },
                    domain = {'row': 0, 'column': 0}
                            )
                )

    fig.update_layout(
    grid = {'rows': 1, 'columns': 1, 'pattern': "independent"},
    template = {'data' : {'indicator': [{
        'title': {'text': "Speed"},
        'mode' : "number+delta+gauge",
        'delta' : {'reference': 90}}]
                         }})
    return fig

def b(e):
    for i  in range (10):
        slider.value += 10
        print ('value changed')
        time.sleep(1)


btn = pn.widgets.Button(name = 'button', width =200)

btn.on_click(b)

pn.Row(pn.Column(btn, slider), fip).show(port=5007)

Thank you very much for the quick response @Marc !!!

I noticed a glitch when the semi-pie chart updates. Its because you put fip in as a function. It’s “slow” as it needs to determine the type of object and transfer a Plotly pane to the browser for each update. If you define a Plotly pane instead and just update the .object it works better.

I’ve created a parametrized example below. I just love the Parameterized api :slight_smile:

I tried to optimize for efficiency as described here https://panel.holoviz.org/reference/panes/Plotly.html but it is not totally clear to me if I did the “perfect” thing.

import time
import panel as pn
import param

pn.extension("plotly")

import plotly.graph_objects as go


class App(param.Parameterized):
    silu = param.Integer(default=0, bounds=(0, 1000))
    click_me = param.Action()
    view = param.Parameter()

    def __init__(self, **params):
        super().__init__(**params)

        self.settings_pane = pn.Param(
            self,
            parameters=["silu", "click_me"],
            sizing_mode="fixed",
            height=400,
            width=300,
            background="lightgrey",
        )
        self.fig = self._get_figure()
        self.figure_pane = pn.pane.Plotly(self.fig, sizing_mode="stretch_width")
        self.view = pn.Column(
            pn.Row(self.settings_pane, self.figure_pane, sizing_mode="stretch_both"),
            sizing_mode="stretch_both",
        )

        self.click_me = self._click_me
        self._update_figure()

    def _get_figure(self):
        fig = go.Figure()
        fig.update_layout(
            grid={"rows": 1, "columns": 1, "pattern": "independent"},
            template={
                "data": {
                    "indicator": [
                        {
                            "title": {"text": "Speed"},
                            "mode": "number+delta+gauge",
                            "delta": {"reference": 90},
                        }
                    ]
                }
            },
        )
        return fig

    @param.depends("silu", watch=True)
    def _update_figure(self):
        self.fig.data=[]
        self.fig.add_trace(
            go.Indicator(
                value=20 * self.silu,
                delta={"reference": 160},
                gauge={"axis": {"visible": False}},
                domain={"row": 0, "column": 0},
            )
        )
        self.figure_pane.object = self.fig.to_dict()

    def _click_me(self, events):
        for i in range(10):
            if self.silu + 10 < 1000:
                self.silu += 10
            else:
                self.silu = 0
            print("value changed")
            time.sleep(0.33)


App().view.servable()

figure (1)

Thank you very much again !!! I noticed the glitch several times in many scripts and I thought it was normal. With your explanation and the object replacement suggestion I could solve the glicth in a lot of differents scripts.

Thank for the explanation and awesome-panel !!!

1 Like