This question is adjacent to a previous question I asked about filtering a table by clicking on a bar segment. I'd like to do the same thing with a sunburst, but don't seem to be able to get the callback to fire off with with on_click or on_hover. I have set up sunburst_chart() in the server function that cycles through traces (there appears to be only one trace for this type of chart) Each trace has on_click and on_hover applied with callback instructions to execute setHoverValues and setClickValues respectively. setHoverValues() and setClickValues() each dump the value of the callback attribute 'points' into a reactive value. The reactive values is display as a text widget. I've tried to set breakpoints on the trace.on_hover() and trace.on_click(), and they stop once during program execution, but don't stop there again. Setting breakpoints anywhere inside setHoverValues() or setClickValues() doesn't do anything -- execution never stops there. from shiny import App, reactive, render, ui from shinywidgets import output_widget, render_widget import plotly.graph_objects as go import plotly.express as px import pandas as pd df=pd.DataFrame({'Category':['Utilities','Rent','Insurance','Gas','Parking','Maintenance','Groceries','Restaurants'], 'Category_L2':['Home','Home','Home','Car','Car','Car','Food','Food']}) app_ui = ui.page_fluid( ui.output_text_verbatim('hoverInfoOutput'), ui.output_text_verbatim('clickInfoOutput'), output_widget('sunburst_chart'), ) def server(input, output, session): hoverInfo = reactive.value({}) clickInfo = reactive.value({}) def setHoverValues(trace, points, selector): if not points.point_inds: return hoverInfo.set(points.copy()) @render.text def hoverInfoOutput(): return hoverInfo.get() def setClickValues(trace, points, selector): if not points.point_inds: return clickInfo.set(points.copy()) @render.text def clickInfoOutput(): return hoverInfo.get() @render_widget def sunburst_chart(): # Prepare data for the Sunburst chart # We need labels, parents, values, and ids for each level labels = [] parents = [] values = [] ids = [] # 1. Add Category_L2 (inner ring) data # Count the occurrences of each Category_L2 to determine its size l2_counts = df['Category_L2'].value_counts() for l2_category, count in l2_counts.items(): labels.append(l2_category) parents.append("") # Category_L2 are root parents for this hierarchy values.append(count) ids.append(f"L2_{l2_category}") # Unique ID for each L2 category # 2. Add Category (outer ring) data # Each Category instance contributes a value of 1 to its parent Category_L2 for index, row in df.iterrows(): category = row['Category'] category_l2 = row['Category_L2'] labels.append(category) parents.append(f"L2_{category_l2}") # Parent is the corresponding Category_L2 values.append(1) # Each individual category entry has a value of 1 ids.append(f"Cat_{category}") # Unique ID for each Category # Create the Plotly Sunburst chart fig = go.Figure(go.Sunburst( ids=ids, labels=labels, parents=parents, values=values, # Set the branch values to 'total' to ensure parent values are sums of children branchvalues="total", # Customize hover info hovertemplate='<b>%{label}</b><br>Count: %{value}<extra></extra>', )) # Update layout to make it a donut chart fig.update_layout( title_text='Two-Level Donut Chart: Category vs. Category_L2', margin=dict(t=50, l=0, r=0, b=0), # Adjust margins for better fit # Set the hole to create the donut effect (0.5 is a good starting point) uniformtext_minsize=10, uniformtext_mode='hide' # Hide text if too small ) figWidget = go.FigureWidget(fig) for trace in fig.data: trace.on_hover(setHoverValues) trace.on_click(setClickValues) return figWidget app = App(app_ui, server) Continue reading...