FigureWidget get trace id


#1

Hi all,

The traces list below has 15 go.Pie objects appended to it. I had to append it because of the incompatibility of go.Pie with subplots.

fig = go.FigureWidget(data = traces, layout = layout)
scat = fig.data[0]
fig

I am using scat’s on_click() method to update another figure. However, I cannot observe which trace on the fig was clicked, given that I am assigning it to the 0th go.Pie of the fig.data.

Is there any solution to assign scat using the index of which trace is clicked? To clarify:

 fig = go.FigureWidget(data = traces, layout = layout)
 idx = _get_which_trace_is_clicked_somehow_
 scat = fig.data[idx]
 fig

Or another workaround solution?

Thanks!


#2

Hi @agahkarakuzu,

I’m not sure I quite understand your question, but here’s an example of detecting click events on multiple pie traces in one figure. As you click around, the trace name and slice index are dislayed below the figure

import plotly.graph_objs as go
from ipywidgets import Output, VBox

fig = go.FigureWidget({
    'data': [
        {
            'labels': ['1st', '2nd', '3rd', '4th', '5th'],
            'values': [38, 27, 18, 10, 7],
            'type': 'pie',
            'name': 'Starry Night',
            'marker': {'colors': ['rgb(56, 75, 126)',
                                  'rgb(18, 36, 37)',
                                  'rgb(34, 53, 101)',
                                  'rgb(36, 55, 57)',
                                  'rgb(6, 4, 4)']},
            'domain': {'x': [0, .48],
                       'y': [0, .49]},
            'hoverinfo':'label+percent+name',
            'textinfo':'none'
        },
        {
            'labels': ['1st', '2nd', '3rd', '4th', '5th'],
            'values': [28, 26, 21, 15, 10],
            'marker': {'colors': ['rgb(177, 127, 38)',
                                  'rgb(205, 152, 36)',
                                  'rgb(99, 79, 37)',
                                  'rgb(129, 180, 179)',
                                  'rgb(124, 103, 37)']},
            'type': 'pie',
            'name': 'Sunflowers',
            'domain': {'x': [.52, 1],
                       'y': [0, .49]},
            'hoverinfo':'label+percent+name',
            'textinfo':'none'

        },
        {
            'labels': ['1st', '2nd', '3rd', '4th', '5th'],
            'values': [38, 19, 16, 14, 13],
            'marker': {'colors': ['rgb(33, 75, 99)',
                                  'rgb(79, 129, 102)',
                                  'rgb(151, 179, 100)',
                                  'rgb(175, 49, 35)',
                                  'rgb(36, 73, 147)']},
            'type': 'pie',
            'name': 'Irises',
            'domain': {'x': [0, .48],
                       'y': [.51, 1]},
            'hoverinfo':'label+percent+name',
            'textinfo':'none'
        },
        {
            'labels': ['1st', '2nd', '3rd', '4th', '5th'],
            'values': [31, 24, 19, 18, 8],
            'marker': {'colors': ['rgb(146, 123, 21)',
                                  'rgb(177, 180, 34)',
                                  'rgb(206, 206, 40)',
                                  'rgb(175, 51, 21)',
                                  'rgb(35, 36, 21)']},
            'type': 'pie',
            'name':'The Night Café',
            'domain': {'x': [.52, 1],
                       'y': [.51, 1]},
            'hoverinfo':'label+percent+name',
            'textinfo':'none'
        }
    ],
    'layout': {'title': 'Van Gogh: 5 Most Prominent Colors Shown Proportionally',
               'showlegend': False}
})

# Capture `print` output of clicked function so 
# we can see it
out = Output()
@out.capture(clear_output=False)
def clicked_pie(trace, points, state):
    if points.point_inds:
        print('clicked %s on slice %d' % (trace.name, points.point_inds[0]))

# install callback on all pie traces
for trace in fig.data:
    if trace.type == 'pie':
        trace.on_click(clicked_pie)

VBox([fig, out])

Hope that helps get you started!
-Jon


#3

Thank you so much, it worked! Now I will try to update the other figure with the trace.name info from print('clicked %s on slice %d' % (trace.name, points.point_inds[0])).


#4

@jmmease here is the issue:

I need to define conditional bindings to those pie traces. Assume that there is a second FigureWidget object fig2, which has a boxplot, whose color is to be updated depending on which trace is clicked in the fig. When I try to assign on_click in the same scope with the following statement:

print('clicked %s on slice %d' % (trace.name, points.point_inds[0]))

then, thefig2 loops through all the events that are supposed to be triggered by the pie traces one by one.

For example, when I click pie trace #1 , I want the boxplot in the fig2to attain green color. For the (fig) pie trace #2 it is red (on fig2), and for the pie #3 it is green.

What happens now is when I click on, let say, pie #2 (which was supposed to make boxplot in the fig2 red), the boxplot flashes all the colors, then attains the last color in my list. This happens no matter which trace I click.

How am I supposed to traffic these events? What I am doing right now looks like this:

out = Output()
@out.capture(clear_output = False)
def clicked_pie(trace,points,state):
        if points.point_inds:
            aa = trace.name # get trace idx 0,1 or 2 
            tmp = aa.find(' ')
            idx = int(aa[tmp:])
            fig.data[idx].on_click(foo) # Bind 'only' the clicked trace, so that foo function can figure out the 
                                                          respective operation. Is this a legit function call here? If not, then where? 


fig.data[0].on_click(clicked_pie)
fig.data[1].on_click(clicked_pie)
fig.data[2].on_click(clicked_pie)

def foo(trace, points, selector):
       if np.array_equal(trace.domain['x'],[0.75, 1]) and np.array_equal(trace.domain['y'],[0, 0.25]):
            fbox.color = 'green'
       if np.array_equal(trace.domain['x'],[0.25, 1]) and np.array_equal(trace.domain['y'],[0, 0.5]):
            fbox.color = 'red'
        if np.array_equal(trace.domain['x'],[0.75, 1]) and np.array_equal(trace.domain['y'],[0, 0.5]):
            fbox.color = 'blue'

fbox = fig2.data[0]
fbox.on_click(foo)

#5

Hi @agahkarakuzu,

If you’d like to change the properties of the box trace depending on which pie trace was clicked I’d recommend you handle the update logic in the pie traces’ on_click method. Here’s an updated example that updates the box trace color depending on which of the 4 pie traces are clicked. Notice that there is on on_click callback installed on the fbox trace

import numpy as np
import plotly.graph_objs as go
from ipywidgets import Output, VBox, HBox

fig = go.FigureWidget({
    'data': [
        {
            'labels': ['1st', '2nd', '3rd', '4th', '5th'],
            'values': [38, 27, 18, 10, 7],
            'type': 'pie',
            'name': 'Starry Night',
            'marker': {'colors': ['rgb(56, 75, 126)',
                                  'rgb(18, 36, 37)',
                                  'rgb(34, 53, 101)',
                                  'rgb(36, 55, 57)',
                                  'rgb(6, 4, 4)']},
            'domain': {'x': [0, .48],
                       'y': [0, .49]},
            'hoverinfo':'label+percent+name',
            'textinfo':'none'
        },
        {
            'labels': ['1st', '2nd', '3rd', '4th', '5th'],
            'values': [28, 26, 21, 15, 10],
            'marker': {'colors': ['rgb(177, 127, 38)',
                                  'rgb(205, 152, 36)',
                                  'rgb(99, 79, 37)',
                                  'rgb(129, 180, 179)',
                                  'rgb(124, 103, 37)']},
            'type': 'pie',
            'name': 'Sunflowers',
            'domain': {'x': [.52, 1],
                       'y': [0, .49]},
            'hoverinfo':'label+percent+name',
            'textinfo':'none'

        },
        {
            'labels': ['1st', '2nd', '3rd', '4th', '5th'],
            'values': [38, 19, 16, 14, 13],
            'marker': {'colors': ['rgb(33, 75, 99)',
                                  'rgb(79, 129, 102)',
                                  'rgb(151, 179, 100)',
                                  'rgb(175, 49, 35)',
                                  'rgb(36, 73, 147)']},
            'type': 'pie',
            'name': 'Irises',
            'domain': {'x': [0, .48],
                       'y': [.51, 1]},
            'hoverinfo':'label+percent+name',
            'textinfo':'none'
        },
        {
            'labels': ['1st', '2nd', '3rd', '4th', '5th'],
            'values': [31, 24, 19, 18, 8],
            'marker': {'colors': ['rgb(146, 123, 21)',
                                  'rgb(177, 180, 34)',
                                  'rgb(206, 206, 40)',
                                  'rgb(175, 51, 21)',
                                  'rgb(35, 36, 21)']},
            'type': 'pie',
            'name':'The Night Café',
            'domain': {'x': [.52, 1],
                       'y': [.51, 1]},
            'hoverinfo':'label+percent+name',
            'textinfo':'none'
        }
    ],
    'layout': {'title': 'Van Gogh: 5 Most Prominent Colors Shown Proportionally',
               'showlegend': False}
})


fbox = go.FigureWidget(data=[go.Box(y=np.random.rand(50))])

colors=['green', 'red', 'blue', 'orange']

# Capture `print` output of clicked function so 
# we can see it
out = Output()
@out.capture(clear_output=False)
def clicked_pie(trace, points, state):
    if points.point_inds:
        print('clicked %s (index %d) on slice %d' % (trace.name, points.trace_index, points.point_inds[0]))
        fbox.data[0].marker.color = colors[points.trace_index]

# install callback on all pie traces
for trace in fig.data:
    if trace.type == 'pie':
        trace.on_click(clicked_pie)

VBox([HBox([fig, fbox]), out])

Hope that helps get you a bit further!
-Jon


#6

Thank you @jmmease, you are the best! This one worked as charm :slight_smile:

I see what caused problem in my case, I created a cascade of on_click events that mixed things up.

Best,
Agah