Several buttons how to know which one is pressed?

Hi,

In the example below. Is it possible to tag the buttons somehow to know which ones are being pushed and only draw area under graph for the respective curve? Buttons can be named with the attribute “name”, is it possible to get the name in the drawArea function?

from bokeh.io import show
from bokeh.layouts import column, row
from bokeh.models import ColorPicker, Div, HTMLTemplateFormatter, ColumnDataSource, TableColumn, DataTable, TextInput, Button
from bokeh.io import curdoc
from bokeh.plotting import figure, output_file, show
import numpy as np
from numpy import trapz
import pandas as pd

output_file("dark_minimal.html")
#curdoc().theme = 'dark_minimal'

plot = figure(width=350, height=350)

d = {'systems': ['Zone-1', 'Zone-2', 'Zone-3'], 
     'colors': ['#18e7b9','#520084','#d2d200'],
     'power': [1.25, 1.5, 1.75]
    }
df = pd.DataFrame(data=d, index = [0, 1, 2])

template="""                
            <span class="bk bk-input-group"> 
            <input class="bk bk-input" type="color" value = <%= value %>> 
            </span>
            """

formatter =  HTMLTemplateFormatter(template=template)

def getData(val, tstart = 0, tend = 10):
    x = np.arange(tstart, tend, 1)
    y = val**x 
    return x, y

def plotter(fig, df):
    plot_dict = {} #use this dictionary to store/organize stuff
    #basically going for this kind of structure: 
    #name/system as keys, each key holding another dictionary pointing to 'fig':thefigureitsplottedon, 'rend':thelinerenderer, and 'picker':itscorrespondingcolorpicker
    for name, color, val in zip(sorted(df.systems), df.colors, df.power):
        print(name, color, val)
        x, y = getData(val)
        
        line = fig.line(x=x, y=y, color=color, line_width=4, legend_label = name)
        
        picker = ColorPicker(title=name,color=color) #added color arg here to properly initialize
        tstart = TextInput(value="0", title="Start time", width = 200)
        tend = TextInput(value="10", title="End time", width = 200)
        drawButton = Button(label="Calculate Area under graph", button_type="success")

        picker.js_link('color', line.glyph, 'line_color')
        plot_dict[name] = {}
        plot_dict[name]['fig'] = fig
        plot_dict[name]['rend'] = line
        plot_dict[name]['picker'] = picker
        plot_dict[name]['tstart'] = tstart
        plot_dict[name]['tend'] = tend
        plot_dict[name]['drawButton'] = drawButton
        
        drawButton.on_click(drawArea)
        
    return plot_dict

def fillColorUndercurve(x, y, plot, tstart, tend, Zone, color):
    print(tstart.value, tend.value)
    area = trapz(y, x, dx=5)
    print("area =", area)
    plot.circle_cross(x, y ,
                      size = 7,
                      legend_label = Zone + ', Area = ' + str(round(area, 2)) + ' mg',
                              color=color,
                              fill_alpha = 1)
    plot.varea(x = x, 
               y1 = 0.000000000000000000001, 
               y2 = y, 
               legend_label = Zone + ', Area = ' + str(round(area, 2)) + ' mg',
               fill_color = color)
    plot.legend.click_policy = 'hide'
    
    return plot

def drawArea(event):
    Zone = 'Zone-1'
    tstart = plot_dict[Zone]['tstart']
    tend = plot_dict[Zone]['tend']
    print(tstart, tend)

    x, y = getData(val = df.power[0], tstart = float(tstart.value), tend = float(tend.value))
    color = df.colors[0]
    fillColorUndercurve(x, y, plot, tstart, tend, Zone, color)
    
plot_dict =  plotter(plot,df)
plot_dict['Zone-1']['drawButton'].on_click(drawArea)
#now just use plot dict to organize a layout/column
lo = row(plot, column([column(plot_dict[k]['picker'], plot_dict[k]['tstart'], plot_dict[k]['tend'], plot_dict[k]['drawButton']) for k in plot_dict.keys()]))

curdoc().add_root(lo)

I haven’t tried it myself, but in case event doesn’t have any information about sender, I guess you might try to use functools.partial to create a new event handler for each button with some fixed argument:

from functools import partial

def drawArea(event, name):
    …

drawButton.on_click(partial(drawArea, name=name))
3 Likes