def _init_edit_bt(self, flag_index): edit_flag_bt = Button(name='edit_flag_bt_{}'.format(flag_index), label='', width=30, tags=['edit_flag_bt'], css_classes=['edit_flag_bt']) def update_flag_value_edit_bt(flag_index=flag_index): self.update_flag_value(flag_value=flag_index, flag_to_update=None, row_indexes=self.env.selection) edit_flag_bt.on_click(update_flag_value_edit_bt) return edit_flag_bt
# # SELECT menu GUI # selectoptions = ["Postive tested on Covid-19 virus", "Negative tested on Covid-19 virus", "Show both"] # resultSelect = Select(title="What to show", options=selectoptions) # TOOL BUTTON CALLBACKS ----------------------------------------------------------------------------------------------- # Test button GUI lab = "Click me!" but = Button(label=lab) def callback_button1( ): # simple test callback -> changes lable on button and reports a click to the console print("button was clicked!") but.on_click(callback_button1) # links the clickedcode to the button # [END] TOOL BUTTON CALLBACKS ----------------------------------------------------------------------------------------- # general webpage & plots title = Div( text= "<b>Visualisation tool of patients tested for Covid-19 of the Hospital Israelita Albert Einstein, at São Paulo, Brazil</b>", style={ 'font-size': '200%', 'color': 'black' }, width=800) text = [title] # gridplot
def modify_doc(doc): """Create and modify the bokeh document. Later run by the tornado server.""" city_1_title = 'Search for the first city' city_2_title = 'Search for the second city' # Adding selectors, essentially the controls, to the app. city_1_select = AutocompleteInput(value=city_1, completions=cities_list, min_characters=3, title=city_1_title) city_2_select = AutocompleteInput(value=city_2, completions=cities_list, min_characters=3, title=city_2_title) variable_select = Select(value=variable, title='Variable', options=list(variable_dict.keys())) method_select = Select(value=method, title='Period', options=methods_list) # And a button to toggle display of trend. display_trend = Button(label='Display trends', button_type='success', width=50) # Initial plot for startup of app. plot = create_plot(source_1, source_2, title, variable, trend) def trend_update(): """Custom callback in the event of button changing state.""" # Make use of global variables. global trend, source_1, source_2, title, variable # Switch state of trend(bool) trend = not trend if trend: display_trend.label = 'Hide trends' else: display_trend.label = 'Show trends' layout.children[1] = create_plot(source_1, source_2, title, variable, trend) def update_plot(attrname, old, new): """This is called when any of the widgets are changed, updates the plot""" # Need to access the value of the global variables. global city_1, city_2, source_1, source_2, title, variable, method # Get the new value of cities and variable. city_1 = city_1_select.value city_2 = city_2_select.value variable = variable_select.value method = method_select.value # Set new title of plot based on new variables. title = (method + ' ' + variable.lower() + ' data from ' + city_1 + ' and ' + city_2 + '.') # Get new source data based on the new parameters. source_1 = get_data(city_1, variable, method) source_2 = get_data(city_2, variable, method) # This is what is actually updating the plot, the plot is the second # child of the layout. layout.children[1] = create_plot(source_1, source_2, title, variable, trend) # Looping through all the selectors to detect changes. control_list = [ city_1_select, city_2_select, variable_select, method_select ] for w in control_list: w.on_change('value', update_plot) # Need to treat button differently. display_trend.on_click(trend_update) # Title displayed on page. text = ('<font size="5">Welcome to the climtrend visualization tool.' ' Compare the climate in two cities by using the controls' ' below.</font>') p = Div(text=text, width=300, height=200) controls = column(p, city_1_select, city_2_select, variable_select, method_select, display_trend) # Add the controls of the app and the plot to the first row of the layout. layout = row(controls, plot) doc.add_root(layout) doc.title = 'ClimTrend'
class BokehDataTable(Environment): ''' DataTable Controls and events ''' env = Environment def __init__(self, **kwargs): lg.info('-- INIT BOKEH DATATABLE') self.env.bk_table = self self.s = 0 # current sample self.ns = 0 # number of samples self.table_df = None self.update_params() self.flag_formatter = None self.value_formatter = None self.param_formatter = None self.env.dt_manual_update = True self._init_templates() self._init_datatable() self._init_sample_buttons() def _init_templates(self): switch_case = '' for c, v in CIRCLE_COLORS.items(): switch_case += "case '{}': return('{}'); break;\n".format( str(c), v) flag_template = """ <div style="font-weight: bold; color: <%= (function colorfromint() {{ switch (value) {{ {} default: return('black'); }} }}()) %>; " ><%= value %> </div> """.format(switch_case) self.flag_formatter = HTMLTemplateFormatter( template=flag_template ) # TODO: to show float numbers NumberFormatter should be used value_template = """ <div style="font-weight: <%= (function colorfromint() { switch (value) { case 'NaN': return('bold'); break default: return('normal'); } }()) %>; color: <%= (function colorfromint() { switch (value) { case 'NaN': return('red'); break default: return('black'); } }()) %>; " ><%= value %> </div> """ # lg.info('>> TEMPLATE: {}'.format(value_template)) self.value_formatter = HTMLTemplateFormatter(template=value_template) param_template = """ <div style="font-weight: bold;"><%= value %></div> """ # lg.info('>> TEMPLATE: {}'.format(param_template)) self.param_formatter = HTMLTemplateFormatter(template=param_template) def _init_datatable(self): columns = [ TableColumn( width=80, field="parameter", title="Parameter", formatter=self.param_formatter, editor=CellEditor() # non editable ), TableColumn(width=50, field="value", title="Value", formatter=self.value_formatter, editor=CellEditor()), TableColumn(width=40, field="flag", title="Flag", formatter=self.flag_formatter, editor=StringEditor()), ] self.table_df = pd.DataFrame( dict( parameter=self.params, value=[''] * len(self.params), flag=[''] * len(self.params), )) table_cds = ColumnDataSource(self.table_df) self.data_table = DataTable( width=190, height=125, source=table_cds, columns=columns, editable= True, # TODO: check if there is a better way than https://stackoverflow.com/a/49424647/4891717 fit_columns=False, # avoids horizontal scrolls bar index_position=None, # hides index column selectable=True, # this is needed to edit cells reorderable= False, # NOTE: this needs jquery ui, but it is not needed scroll_to_selection=False, # not needed sortable=False, # not needed ) self.data_table.source.on_change('data', self.on_change_data_source) def _init_sample_buttons(self): lg.info('-- SET SAMPLE BUTTONS') def next_sample(): lg.info('>> NEXT SAMPLE') if self.s < self.ns: self.env.doc.hold('collect') self.s += 1 self.sample_div.text = ' {} / {}'.format(self.s, self.ns) self.env.cur_nearby_prof = None # to reset the extra stt profile to plot self.env.dt_next_sample = True self._update_dt_sample() self.env.doc.unhold() def previous_sample(): lg.info('>> PREVIOUS SAMPLE') if self.s > 1: self.env.doc.hold('collect') self.s -= 1 self.sample_div.text = ' {} / {}'.format(self.s, self.ns) self.env.cur_nearby_prof = None self.env.dt_previous_sample = True self._update_dt_sample() self.env.doc.unhold() self.next_bt = Button(label=">", button_type="success", width=30) self.sample_div = Div( text='0 / 0', width=100, height=30, css_classes=['sample_div'], ) self.previous_bt = Button(label="<", button_type="success", width=30) self.next_bt.on_click(next_sample) self.previous_bt.on_click(previous_sample) def update_params(self): self.params = self.env.cruise_data.get_cols_by_type(['param']) self.params.insert(0, STNNBR) def update_dt_source(self): ''' Updates the datatable source depending on the selected samples ''' lg.info('-- UPDATE DATATABLE SOURCE') self.update_params() df = self.env.cds_df.iloc[self.env.selection] df_filter = self.env.cruise_data.get_cols_by_type( ['param', 'param_flag', 'qc_param_flag']) df_filter.insert(0, STNNBR) df = df.filter(df_filter) self.table_df = df # TODO: check if it has the same order as the selection) self.ns = len(self.env.selection) self.s = 1 if self.env.selection != [] else 0 self.sample_div.text = ' {} / {}'.format(self.s, self.ns) self._update_dt_sample() def _update_dt_sample(self): ''' Updates the datatable with the data of the self.s ''' lg.info('-- UPDATE DT SAMPLE') flag_values = [] param_values = [] if self.s != 0: self.env.sample_to_select = self.env.selection[self.s - 1] for p in self.params: v = self.table_df.loc[self.env.sample_to_select, p] # lg.info('>> V: {} | TYPE: {}'.format(v, type(v))) if not isinstance(v, str): if np.isnan(v): v = 'NaN' else: v = str(v) param_values.append(v) fp = p + FLAG_END if fp in self.table_df.columns: fv = self.table_df.loc[self.env.sample_to_select, fp] if not isinstance(fv, str): if np.isnan(fv): fv = 'NaN' else: fv = str(fv) flag_values.append(fv) else: flag_values.append('-') else: self.env.sample_to_select = None param_values = [''] * len(self.params) flag_values = [''] * len(self.params) new_vals_dict = { 'parameter': self.params, 'value': param_values, 'flag': flag_values } lg.info('>> NEW VALS DICT: \n\n{}'.format(new_vals_dict)) lg.info('>> LENS (PARAMETER, VALUE, FLAG) = ({}, {}, {})'.format( len(self.params), len(param_values), len(flag_values))) lg.info('>> DT_MANUAL_UPDATE IN _UPDATE_DT_SAMPLE: {}'.format( self.env.dt_manual_update)) # self.env.dt_manual_update = False # just in case self.data_table.source.data = new_vals_dict # self.env.dt_manual_update = True self.env.bk_sources.update_prof_sources(force_selection=True) def on_change_data_source(self, attr, old, new): indices = list(range(self.table_df.size)) # changes = [(param_name, index, old_value, new_value)] changes = [(self.params[i], i, j, k) for i, j, k in zip(indices, old['flag'], new['flag']) if j != k] # lg.info('>> CHANGES: {}'.format(changes)) # lg.info('>> SELECTION = {} | DF_MANUAL_UPDATE = {}'.format(self.env.selection, self.env.dt_manual_update)) # lg.info('>> DT_NEXT_SAMPLE = {} | DF_PREVIOUS_SAMPLE = {}'.format(self.env.dt_next_sample, self.env.dt_previous_sample)) error = False # This happens when the DataFrame is empty (Reset button or the initial state) if self.env.selection == [] and self.env.dt_manual_update: error = True if self.env.selection != [] and self.env.dt_manual_update and changes != []: # if len(changes) == 1: # NOTE: if it is a manual update the length should be 1 # but I am getting size 1 when the whole DataTable CDS is updated # I do not why is this happening if not (self.env.dt_next_sample or self.env.dt_previous_sample): for t in changes: if t[2] == '-': error = True self.env.bk_bridge.error_js( 'This parameter does not have any flag column associated.' ) else: try: new_flag_val = int(t[3]) except: error = True self.env.bk_bridge.error_js( 'The value should be an integer.') else: if new_flag_val not in self.env.bk_flags.all_flags_list: error = True self.env.bk_bridge.error_js( 'The cell value should be a valid flag value.' ) else: self.env.dt_manual_update = False self.env.bk_flags.update_flag_value( flag_to_update=t[0] + FLAG_END, flag_value=new_flag_val, row_indexes=[self.env.sample_to_select]) self.env.dt_manual_update = True else: self.env.dt_next_sample = False self.env.dt_previous_sample = False if error: self.rollback(changes) def rollback(self, changes): lg.info('-- DATATABLE ROLLBACK') if self.env.selection != []: for t in changes: patch = { 'flag': [ (t[1], t[2]), ] } # rollback to the old value self.env.dt_manual_update = False self.data_table.source.patch(patch) self.env.dt_manual_update = True else: if changes != []: for t in changes: patch = { 'flag': [ (t[1], ''), ] } # rollback to the initial value self.env.dt_manual_update = False self.data_table.source.patch(patch) self.env.dt_manual_update = True else: self.env.dt_manual_update = False self.table_df = pd.DataFrame( dict( parameter=self.params, # remove flags from here value=[''] * len(self.params), flag=[''] * len(self.params), )) self.data_table.source.data = self.table_df.to_dict('list') self.env.dt_manual_update = True
class BokehEvents(Environment): ''' Controls and events. The widgets and buttons are created here Also some other events and triggers such as update points selections ''' env = Environment def __init__(self, **kwargs): lg.info('-- INIT BOKEH EVENTS') self.env.bk_events = self self.env.source.selected.on_change('indices', self._update_selection) self.env.wmts_map_source.selected.on_change('indices', self._update_map_selection) self._init_cb_prof_invsbl_points() self._init_profile_nav() self._init_nearby_prof_cb() self._init_tabs() def _update_selection(self, attr, old, new_indices): ''' This is run when some elements are selected: @attr: 'selected' @old: >> does not work well yet with the new Selection class? @new: >> new_indices store the new selection indices list ''' lg.info('-- UPDATE SELECTION: {}'.format(new_indices)) self.env.dt_manual_update = False if self.env.selection != new_indices and new_indices != []: self.env.selection = new_indices self.env.sample_to_select = None # this is reselected later on `_upd_prof_srcs` self.env.stt_to_select = None self.env.cur_nearby_prof = None self.env.cur_partial_stt_selection = [] self._update_map_selection_prog(new_indices) self.env.bk_table.update_dt_source( ) # prof sources is updated inside self.env.reset_selection = False elif self.env.selection != [] and new_indices == []: # NOTE: Keeps the selection when the user click on a space without any sample if self.env.reset_selection: lg.info('>> RESET SELECTION') self.env.selection = [] self.env.sample_to_select = None # this is reselected later on `_upd_prof_srcs` self.env.stt_to_select = None self.env.cur_nearby_prof = None self.env.cur_partial_stt_selection = [] self.env.reset_selection = False else: lg.info('>> KEEP SELECTION') self.env.source.selected.indices = self.env.selection # keep selection self.env.dt_manual_update = True # reactivate the manual update of the datatable def _update_map_selection(self, attr, old, new_indices): lg.info('-- UPDATE MAP SELECTION') if self.env.map_selection != new_indices and new_indices != []: # this can be triggered by the user, and programmatically?? lg.info('>> NEW MAP SELECTION: {}'.format(new_indices)) self.env.map_selection = new_indices # triggers the _update_selection # lg.info('>> MAP SOURCE: {}'.format(self.env.wmts_map_source.data)) selected_stts = list( self.env.wmts_map_source.data[STNNBR][new_indices]) # self.env.cur_partial_stt_selection = selected_stts # TODO: how to get all the indices of the current selected stations?? by self.env.cds_df is updated?? sel_inds = list(self.env.cds_df[self.env.cds_df[STNNBR].isin( selected_stts)].index) self.env.source.selected.indices = sel_inds self._update_selection( attr='selected', old=None, new_indices=self.env.source.selected. indices # I need to trigger this manually as well ) elif self.env.map_selection != [] and new_indices == []: if self.env.reset_selection: lg.info('>> RESET MAP SELECTION') self.env.map_selection = [] self.env.wmts_map_source.selected.indices = [] else: lg.info('>> KEEP MAP SELECTION') self.env.wmts_map_source.selected.indices = self.env.map_selection def _update_map_selection_prog(self, new_indices): ''' Updates the map selection programmatically with the stations of the selected samples @new_indices: list of indices of the selected samples (cds_df) ''' lg.info('-- UPDATE MAP SELECTION PROGRAMMATICALLY') if self.env.map_selection != new_indices and new_indices != []: # this can be triggered by the user, and programmatically?? stt_to_select = list(self.env.cds_df.loc[new_indices, STNNBR]) indices_to_select = list(self.env.wmts_map_df[ self.env.wmts_map_df[STNNBR].isin(stt_to_select)].index) self.env.map_selection = indices_to_select self.env.wmts_map_source.selected.indices = indices_to_select elif self.env.map_selection != [] and new_indices == []: if self.env.reset_selection: lg.info('>> RESET MAP SELECTION') self.env.map_selection = [] self.env.wmts_map_source.selected.indices = [] def _init_cb_prof_invsbl_points(self): ''' Plot the profiles with the visible points or with the invisible as well ''' def on_click_cb_prof_invsbl_points(active_list): if active_list == [0]: self.env.plot_prof_invsbl_points = True else: self.env.plot_prof_invsbl_points = False self.env.bk_sources._upd_prof_srcs() self.cb_prof_invsbl_points = CheckboxGroup( width=200, height=10, labels=['Fixed profiles'], # Plot invisible points on profiles active=[], css_classes=['fixed_profiles_cb', 'bokeh_hidden']) self.cb_prof_invsbl_points.on_click(on_click_cb_prof_invsbl_points) def _init_profile_nav(self): def next_profile(): if self.nearby_prof_cb: lg.info('-- NEXT PROFILE') s = self.env.stations next_pos = s.index(self.env.cur_nearby_prof) + 1 if next_pos < len(self.env.stations): if s[next_pos] == self.env.stt_to_select: next_pos = next_pos + 1 if next_pos < len(self.env.stations): self.env.cur_nearby_prof = s[next_pos] self.env.bk_sources._upd_prof_srcs(force_selection=True) self.nearby_prof_div.text = str( int(self.env.cur_nearby_prof)) # adjust disabled buttons if next_pos + 1 == len(self.env.stations): self.next_prof_bt.disabled = True self.previous_prof_bt.disabled = False def previous_profile(): lg.info('-- PREVIOUS PROFILE') if self.nearby_prof_cb: s = self.env.stations previous_pos = s.index(self.env.cur_nearby_prof) - 1 if previous_pos >= 0: if s[previous_pos] == self.env.stt_to_select: previous_pos = previous_pos - 1 if previous_pos >= 0: self.env.cur_nearby_prof = s[previous_pos] self.env.bk_sources._upd_prof_srcs(force_selection=True) self.nearby_prof_div.text = str( int(self.env.cur_nearby_prof)) # adjust disabled buttons if previous_pos == 0: self.previous_prof_bt.disabled = True self.next_prof_bt.disabled = False self.next_prof_bt = Button( width=30, disabled=True, label=">", button_type="success", ) self.nearby_prof_div = Div( width=100, height=30, text='None', css_classes=['cur_stt'], ) self.previous_prof_bt = Button( width=30, disabled=True, label="<", button_type="success", ) self.next_prof_bt.on_click(next_profile) self.previous_prof_bt.on_click(previous_profile) def _init_nearby_prof_cb(self): def on_click_nearby_prof(active_list): lg.info('-- ONCLICK NEARBY PROF') lg.info('>> SELECTED STT: {}'.format(self.env.stt_to_select)) if 0 in active_list: self.env.plot_nearby_prof = True self.set_cur_nearby_prof() self.env.bk_sources._upd_prof_srcs(force_selection=True) else: self.env.plot_nearby_prof = False self.next_prof_bt.disabled = True self.previous_prof_bt.disabled = True self.env.cur_nearby_prof = None self.nearby_prof_div.text = 'None' self.env.bk_sources._upd_prof_srcs(force_selection=True) self.nearby_prof_cb = CheckboxGroup( width=200, height=20, labels=['Show nearby station'], active=[], css_classes=['show_nearby_station_cb', 'bokeh_hidden']) self.nearby_prof_cb.on_click(on_click_nearby_prof) def set_cur_nearby_prof(self): lg.info('-- SET CUR NEARBY PROF') self.next_prof_bt.disabled = False # TODO: if the database has only one station? self.previous_prof_bt.disabled = False # NOTE: get the default extra station: the next one if exists # if not, the previous one if self.env.stt_to_select is not None: next_pos = self.env.stations.index(self.env.stt_to_select) + 1 if next_pos < len(self.env.stations): self.env.cur_nearby_prof = self.env.stations[next_pos] self.nearby_prof_div.text = str(int(self.env.cur_nearby_prof)) else: previous_pos = self.env.stations.index( self.env.stt_to_select) - 1 if previous_pos >= 0: self.env.cur_nearby_prof = self.env.stations[previous_pos] self.nearby_prof_div.text = str( int(self.env.cur_nearby_prof)) def _init_tabs(self): lg.info('-- INIT TABS') panel_list = [] # lg.info('>> self.env.TABS_FLAGS_PLOTS: {}'.format(self.env.tabs_flags_plots)) SORT_TABS = False if SORT_TABS: ordered_tab_list = sorted(self.env.tabs_flags_plots) else: ordered_tab_list = list(self.env.tabs_flags_plots.keys()) self.env.cur_tab = ordered_tab_list[ 0] # self.env.cur_tab initialization self.env.cur_flag = self.env.cur_tab + FLAG_END # self.env.cur_tab initialization ly_settings = self.env.f_handler.get_layout_settings() for tab in ordered_tab_list: indices = self.env.tabs_flags_plots[tab]['plots'] children = [ x.plot for x in self.env.bk_plots if x.n_plot in indices ] # lg.info('>> CHILDREN: {}'.format(children)) gp = gridplot( children=children, ncols=ly_settings['ncols'], plot_width=ly_settings[ 'plot_width'], # if 350 then the points are blurred plot_height=ly_settings['plot_height'], toolbar_location= 'left', # TODO: separate the toolbars to set some tools active by default, # like this the hover icon can be shown as well ) name = 'panel_{}'.format(tab.lower()) panel_list.append( Panel( name='panel_{}'.format(tab.lower()), child=gp, title=tab, )) # TODO: closable=True lg.info('>> TABS WIDGET: {}'.format(self.env.tabs_widget)) if self.env.tabs_widget is None: self.env.tabs_widget = Tabs( name='tabs_widget', tabs=panel_list, width=1250, ) else: self.env.tabs_widget.tabs.clear() self.env.tabs_widget.tabs = panel_list def update_active_tab(attr, old, new): lg.info('-- UPDATE ACTIVE TAB | OLD: {} | NEW: {}'.format( old, new)) self.env.cur_tab = self.env.tabs_widget.tabs[new].title lg.info('>> CUR TAB: {}'.format(self.env.cur_tab)) flag = self.env.tabs_flags_plots[self.env.cur_tab]['flag'] if self.env.flagger_select.value != flag: self.env.tab_change = True self.env.flagger_select.value = flag # if they concide the update of the select is not triggered self.env.tabs_widget.on_change('active', update_active_tab)
labeling_input = widgetbox(labeling_x_mu, labeling_y_mu, labeling_x_var, labeling_y_var, labeling_thr) inputs = [hijacking_input, good_user_input, labeling_input] # Plot the ROC curve roc_source = ColumnDataSource(data=dict(x=[], y=[])) def train_and_evaluate(): global hijackers, good_users, labeling, roc_source fpr, tpr = calculate_roc(hijackers, good_users, labeling) roc_source.data = dict(x=fpr, y=tpr) train_and_evaluate() roc = figure(title='RoC', x_axis_label='False positive rate', y_axis_label='True Positive Rate', x_range=(0, 1), y_range=(0, 1), width=1000) roc.line([0, 1], [0, 1], color='navy', line_width=1.0, line_dash='dashed') roc.line(x='x', y='y', source=roc_source, color='darkorange', line_width=1.0, line_dash='dashed') button = Button(label='Calculate ROC') button.on_click(train_and_evaluate) # Add the simulation and ROC curve to DOM. curdoc().add_root(column(row(space_plot, *inputs), row(roc, widgetbox(button)))) curdoc().title = 'Recall vs accuracy trade-off'
class BokehFlags(Environment): ''' Class to manage Flag Controls and its Events ''' env = Environment @property def all_flags_list(self): # NOTE: current available flags on the CDS plus flags with 0 elements return sorted([i for i, fg_str in self.env.all_flags.items()]) # TODO: If you want to assign a new flag value out of this list, # a mechanism to add a new value should be added def __init__(self, **kwargs): self.env.bk_flags = self self.env.visible_flags = self.all_flags_list self.all_flags_vb_bt = None self.flags_control_header_row = None self.flag_rows = [] self._init_flagger_select() self._init_flags_control_header() self._init_flags_control_table() def _init_flagger_select(self): lg.info('-- INIT FLAGGER SELECT') options = self.env.cruise_data.get_cols_by_type( ['param_flag', 'qc_param_flag']) options = sorted(options) self.env.flagger_select = Select( value=self.env.cur_flag, options=options, css_classes=['flagger_select'], ) def update_select_flag_value(attr, old, new): lg.info('-- SELECT VALUE | OLD: {} | NEW: {}'.format(old, new)) self.env.cur_flag = new if self.env.tab_change: self.env.tab_change = False else: lg.info('-- SELECT CHANGE') self.env.bk_bridge.call_js({ 'object': 'tools', 'function': 'show_wait_cursor', }) self.env.tabs_flags_plots[self.env.cur_tab]['flag'] = new # TODO: replot of all the colors of the tab # some of the glyphs could be invisible # only an indices update is needed cur_plot_list = self.env.tabs_flags_plots[ self.env.cur_tab]['plots'] self.env.doc.hold('collect') self.env.bk_plots_handler.replot_color_circles( only_cur_tab=True) self.env.bk_sources._upd_prof_srcs( ) # TODO: keep the selection as it is >> keep_selection = True # I do this here because some point could be invisible for # other tab self.env.doc.unhold() self.env.bk_bridge.call_js({ 'object': 'tools', 'function': 'show_default_cursor', }) self.env.flagger_select.on_change('value', update_select_flag_value) def _init_flags_control_header(self): lg.info('-- FLAGS CONTROL HEADER') self.all_flags_vb_bt = Button( name='all_flags_bt', label='', width=30, css_classes=['eye_bt'], ) def all_flags_vb_bt_callback(): lg.info('-- ALL FLAGS VISIBLE CALLBACK') self.env.bk_bridge.call_js({ 'object': 'tools', 'function': 'show_wait_cursor', }) all_flags_bt = self.env.doc.select_one(dict(name='all_flags_bt')) eye_slash_bt = True if 'eye_slash_bt' in all_flags_bt.css_classes else False if eye_slash_bt: self.env.doc.set_select(selector=dict(tags=['vb_bt']), updates=dict(css_classes=['eye_bt'])) else: self.env.doc.set_select( selector=dict(tags=['vb_bt']), updates=dict(css_classes=['eye_slash_bt'])) new_visible_flags = [] if 'eye_bt' in all_flags_bt.css_classes: all_flags_bt.css_classes = ['eye_slash_bt'] else: new_visible_flags = self.all_flags_list all_flags_bt.css_classes = ['eye_bt'] lg.info('>> NEW VISIBLE FLAGS: {}'.format(new_visible_flags)) self._update_visible_flags(new_visible_flags) self.env.bk_bridge.call_js({ 'object': 'tools', 'function': 'show_default_cursor', }) self.all_flags_vb_bt.on_click(all_flags_vb_bt_callback) # TODO: replace this div with the flag selection dropdown # or maybe there would be too many control on one place flag_controls_title_div = Div( name='flag_controls_title', text='All the flags', width=100, height=25, css_classes=['flag_controls_title'], ) self.flags_control_header_row = row( children=[self.all_flags_vb_bt, flag_controls_title_div], width=200, height=25, ) def _init_flags_control_table(self): ''' Reminder: self.env.all_flags = { 2: 'FLAG 2', 3: 'FLAG 3', ... } ''' lg.info('-- INIT FLAGS CONTROL TABLE') # lg.info('-- ALL FLAGS DICTIONARY: {}'.format(self.env.all_flags)) for flag_index, str_value in self.env.all_flags.items(): def change_flag_vb(flag_index=flag_index): self.env.bk_bridge.call_js({ 'object': 'tools', 'function': 'show_wait_cursor', }) vb_bt_to_change = self.env.doc.select_one( dict(name='flag_vb_bt_{}'.format(flag_index))) lg.info('>> CHANGING VISIBILITY: {}'.format( 'flag_vb_bt_{}'.format(flag_index))) new_visible_flags = self.env.visible_flags.copy() if 'eye_bt' in vb_bt_to_change.css_classes: new_visible_flags.remove(flag_index) vb_bt_to_change.css_classes = ['eye_slash_bt'] else: new_visible_flags.append(flag_index) vb_bt_to_change.css_classes = ['eye_bt'] self._update_visible_flags(new_visible_flags) self.env.bk_bridge.call_js({ 'object': 'tools', 'function': 'show_default_cursor', }) vb_bt = Button( name='flag_vb_bt_{}'.format(flag_index), label='', width=30, tags=['vb_bt'], css_classes=['eye_bt'], ) vb_bt.on_click(change_flag_vb) edit_flag_bt = self._init_edit_bt(flag_index) fg_str_div = Div(name='fg_str_div_{}'.format(flag_index), text='{}'.format(str_value), width=100, height=25, tags=['fg_str_div'], css_classes=['fg_str_div'], style={ 'color': CIRCLE_COLORS[flag_index], 'font-weight': 'bold', }) flag_row = row( name='flag_row_{}'.format(flag_index), children=[vb_bt, edit_flag_bt, fg_str_div], width=200, height=25, ) # self.env.flag_vb_bts.append(vb_bt) self.flag_rows.append(flag_row) self.env.flags_control_col = column( name='flags_control_col', children=[self.flags_control_header_row] + self.flag_rows, css_classes=['flags_control_col'], ) def _init_edit_bt(self, flag_index): edit_flag_bt = Button(name='edit_flag_bt_{}'.format(flag_index), label='', width=30, tags=['edit_flag_bt'], css_classes=['edit_flag_bt']) def update_flag_value_edit_bt(flag_index=flag_index): self.update_flag_value(flag_value=flag_index, flag_to_update=None, row_indexes=self.env.selection) edit_flag_bt.on_click(update_flag_value_edit_bt) return edit_flag_bt def update_flag_value(self, flag_value=None, flag_to_update=None, row_indexes=[]): lg.info('-- UPDATE FLAG VALUE') self.env.bk_bridge.call_js({ 'object': 'tools', 'function': 'show_wait_cursor', }) if flag_value is None: lg.error( '>> An empty flag value got to `self.env.bk_flags.update_flag_value()`' ) if flag_to_update is None: flag_to_update = self.env.cur_flag if row_indexes == []: lg.error('>> NO row_indexes selected') self.env.cruise_data.update_flag_values( column=flag_to_update, new_flag_value=flag_value, row_indices=row_indexes, ) new_values = np.array(self.env.source.data[flag_to_update], dtype=int) # TODO: Int8 or Int64 new_values[row_indexes] = flag_value self.env.source.data[flag_to_update] = new_values self.env.bk_sources.cds_df[flag_to_update] = new_values # Updating flag colors self.env.doc.hold('collect') self.env.bk_plots_handler.replot_color_circles() # NOTE: update datatable and prof sources is needed because the new flag could be invisible, # then the profiles should be invisible as well # self.env.bk_table.update_dt_source() self.env.bk_sources._upd_prof_srcs(force_selection=True) self.env.doc.unhold() self.env.bk_bridge.call_js({ 'object': 'tools', 'function': 'show_default_cursor', }) self.env.bk_bridge.call_js({ 'object': 'tools', 'function': 'show_snackbar', 'params': [ '{} values of {} updated with the flag value {}'.format( len(row_indexes), flag_to_update, flag_value, ) ], }) def _update_visible_flags(self, to_visible_flags=[]): ''' Makes visible the flags passed as argument, and make invisible the rest @to_visible_flags: all the visible (or to make visible) flags indices ''' lg.info('-- UPDATE VISIBLE FLAGS') to_visible = [] to_invisible = [] for flag_index, flag_str in self.env.all_flags.items(): if flag_index in self.env.visible_flags and flag_index not in to_visible_flags: to_invisible.append('GR_FLAG_{}'.format(flag_index)) if flag_index not in self.env.visible_flags and flag_index in to_visible_flags: to_visible.append('GR_FLAG_{}'.format(flag_index)) # lg.info('>> TO VISIBLE FLAGS: {}'.format(to_visible_flags)) # lg.info('>> TO VISIBLE: {}'.format(to_visible)) # lg.info('>> TO INVISIBLE: {}'.format(to_invisible)) self.env.doc.hold('collect') if to_visible != []: self.env.doc.set_select(selector=dict(tags=to_visible), updates=dict(visible=True)) if to_invisible != []: self.env.doc.set_select(selector=dict(tags=to_invisible), updates=dict(visible=False)) all_flags_bt = self.env.doc.select_one(dict(name='all_flags_bt')) if to_visible_flags == []: all_flags_bt.css_classes = ['eye_slash_bt'] else: all_flags_bt.css_classes = ['eye_bt'] self.env.visible_flags = to_visible_flags.copy() self.env.bk_sources._upd_prof_srcs(force_selection=True) self.env.doc.unhold() def reset_all_flags(self): lg.info('-- RESET ALL FLAGS') if sorted(self.env.visible_flags) != self.all_flags_list: self.env.doc.set_select(selector=dict(tags=['vb_bt']), updates=dict(css_classes=['eye_bt'])) to_visible = [] for flag_index, flag_str in self.env.all_flags.items(): if flag_index not in self.env.visible_flags: to_visible.append('GR_FLAG_{}'.format(flag_index)) self.env.doc.set_select(selector=dict(tags=to_visible), updates=dict(visible=True)) self.env.visible_flags = self.all_flags_list all_flags_bt = self.env.doc.select_one(dict(name='all_flags_bt')) all_flags_bt.css_classes = ['eye_bt']
def _init_flags_control_table(self): ''' Reminder: self.env.all_flags = { 2: 'FLAG 2', 3: 'FLAG 3', ... } ''' lg.info('-- INIT FLAGS CONTROL TABLE') # lg.info('-- ALL FLAGS DICTIONARY: {}'.format(self.env.all_flags)) for flag_index, str_value in self.env.all_flags.items(): def change_flag_vb(flag_index=flag_index): self.env.bk_bridge.call_js({ 'object': 'tools', 'function': 'show_wait_cursor', }) vb_bt_to_change = self.env.doc.select_one( dict(name='flag_vb_bt_{}'.format(flag_index))) lg.info('>> CHANGING VISIBILITY: {}'.format( 'flag_vb_bt_{}'.format(flag_index))) new_visible_flags = self.env.visible_flags.copy() if 'eye_bt' in vb_bt_to_change.css_classes: new_visible_flags.remove(flag_index) vb_bt_to_change.css_classes = ['eye_slash_bt'] else: new_visible_flags.append(flag_index) vb_bt_to_change.css_classes = ['eye_bt'] self._update_visible_flags(new_visible_flags) self.env.bk_bridge.call_js({ 'object': 'tools', 'function': 'show_default_cursor', }) vb_bt = Button( name='flag_vb_bt_{}'.format(flag_index), label='', width=30, tags=['vb_bt'], css_classes=['eye_bt'], ) vb_bt.on_click(change_flag_vb) edit_flag_bt = self._init_edit_bt(flag_index) fg_str_div = Div(name='fg_str_div_{}'.format(flag_index), text='{}'.format(str_value), width=100, height=25, tags=['fg_str_div'], css_classes=['fg_str_div'], style={ 'color': CIRCLE_COLORS[flag_index], 'font-weight': 'bold', }) flag_row = row( name='flag_row_{}'.format(flag_index), children=[vb_bt, edit_flag_bt, fg_str_div], width=200, height=25, ) # self.env.flag_vb_bts.append(vb_bt) self.flag_rows.append(flag_row) self.env.flags_control_col = column( name='flags_control_col', children=[self.flags_control_header_row] + self.flag_rows, css_classes=['flags_control_col'], )
class PSUGUI(TabbedGUI): """@brief Responsible for plotting data on tab 0 with no other tabs.""" CFG_FILENAME = ".RS310P_GUI.cfg" AMPS = "amps" VOLTS = "volts" PLOT_SECONDS = "plotSeconds" CFG_DICT = {VOLTS: "5", AMPS: "1", PLOT_SECONDS: 300} def __init__(self, docTitle, bokehPort=12000): """@Constructor""" super().__init__(docTitle, bokehPort=bokehPort) self._figTable = [[]] self._grid = None self._textBuffer = "" self._psu = None self._on = False def info(self, msg): """@brief Display an info level message.""" self.statusBarWrapper.setStatus("INFO: " + msg) def error(self, msg): """@brief Display an error level message.""" self.statusBarWrapper.setStatus("ERROR: " + msg) def debug(self, msg): """@brief Display an error level message.""" pass def addRow(self): """@brief Add an empty row to the figures.""" self._figTable.append([]) def addToRow(self, fig): """@brief Add a figure to the end of the current row of figues. @param fig The figure to add.""" self._figTable[-1].append(fig) def createPlot( self, doc, ): """@brief create a plot figure. @param doc The document to add the plot to.""" self._doc = doc self._doc.title = "RS310P PSU Controller" self.statusBarWrapper = StatusBarWrapper() self._pconfig = ConfigManager(self, PSUGUI.CFG_FILENAME, PSUGUI.CFG_DICT) self._pconfig.load() plotPanel = self._getPlotPanel() self._tabList.append( Panel(child=plotPanel, title="DC Power Supply Control")) self._doc.add_root(Tabs(tabs=self._tabList)) self._doc.add_periodic_callback(self._viewUpdate, 500) def _viewUpdate(self): if self._on: volts, amps, watts = self._psu.getOutputStats() self._opTableWrapper.setRows([[volts, amps, watts]]) self._updatePlot(volts, amps, watts) def _updatePlot(self, volts, amps, watts): """@brief called periodically to update the plot trace.""" plotPoints = self.plotHistorySpinner.value * 2 now = datetime.now() newVolts = {'x': [now], 'y': [volts]} self._voltsSource.stream(newVolts, rollover=plotPoints) newAmps = {'x': [now], 'y': [amps]} self._ampsSource.stream(newAmps, rollover=plotPoints) newWatts = {'x': [now], 'y': [watts]} self._wattsSource.stream(newWatts, rollover=plotPoints) def _getPlotPanel(self): """@brief Add tab that shows plot data updates.""" self._figTable.append([]) self._voltsSource = ColumnDataSource({'x': [], 'y': []}) self._ampsSource = ColumnDataSource({'x': [], 'y': []}) self._wattsSource = ColumnDataSource({'x': [], 'y': []}) fig = figure(toolbar_location='above', x_axis_type="datetime", x_axis_location="below") fig.line(source=self._voltsSource, line_color="blue", legend_label="Volts") fig.line(source=self._ampsSource, line_color="green", legend_label="Amps") fig.line(source=self._wattsSource, line_color="red", legend_label="Watts") fig.legend.location = 'top_left' self._figTable[-1].append(fig) self._grid = gridplot(children=self._figTable, sizing_mode='scale_both', toolbar_location='right') self.selectSerialPort = Select(title="Serial Port:") self.selectSerialPort.options = glob.glob('/dev/ttyU*') self.outputVoltageSpinner = Spinner(title="Output Voltage (Volts)", low=0, high=40, step=0.5, value=self._pconfig.getAttr( PSUGUI.VOLTS)) self.currentLimitSpinner = Spinner(title="Currnet Limit (Amps)", low=0, high=10, step=0.25, value=self._pconfig.getAttr( PSUGUI.AMPS)) self.plotHistorySpinner = Spinner(title="Plot History (Seconds)", low=1, high=10000, step=1, value=self._pconfig.getAttr( PSUGUI.PLOT_SECONDS)) self._setButton = Button(label="Set") self._setButton.on_click(self._setHandler) self._setButton.disabled = True self._onButton = Button(label="On") self._onButton.on_click(self._psuOnHandler) shutdownButtonWrapper = ShutdownButtonWrapper(self._quit) controlPanel = column([ self.selectSerialPort, self._onButton, self.outputVoltageSpinner, self.currentLimitSpinner, self._setButton, self.plotHistorySpinner, shutdownButtonWrapper.getWidget() ]) self._opTableWrapper = ReadOnlyTableWrapper(("volts", "amps", "watts"), heightPolicy="fixed", height=65, showLastRows=0) plotPanel = column([self._grid, self._opTableWrapper.getWidget()]) panel2 = row([controlPanel, plotPanel]) plotPanel = column([panel2, self.statusBarWrapper.getWidget()]) return plotPanel def _quit(self): if self._on: self._psuOff() self._run(self._delayedShutdown) self._doc.clear() def _delayedShutdown(self): """@brief Allow time for browser page to clear before shutdown.""" volts = self.outputVoltageSpinner.value amps = self.currentLimitSpinner.value self._pconfig.addAttr(PSUGUI.VOLTS, volts) self._pconfig.addAttr(PSUGUI.AMPS, amps) self._pconfig.addAttr(PSUGUI.PLOT_SECONDS, self.plotHistorySpinner.value) self._pconfig.store() sleep(0.5) self._sendUpdateEvent( PSUGUIUpdateEvent(PSUGUIUpdateEvent.SHUTDOWN_SERVER)) def _psuOnHandler(self): """@brief event handler.""" #Stop the user from clicking the button again until this click has been processed. self._onButton.disabled = True #Turn the PSUon/off method outside GUI thread self._run(self._powerOnOff) def _setHandler(self): """@brief event handler.""" #Turn the PSUon/off method outside GUI thread self._run(self._setPSU) def _rxUpdateEvent(self, updateEvent): """@brief Receive an event into the GUI context to update the GUI. @param updateEvent An PSUGUIUpdateEvent instance.""" if updateEvent.id == PSUGUIUpdateEvent.UPDATE_STATUS_TEXT: self.statusBarWrapper.setStatus(updateEvent.argList[0]) elif updateEvent.id == PSUGUIUpdateEvent.CONNECTING_TO_PSU: self._onButton.button_type = "success" self._onButton.disabled = True self.statusBarWrapper.setStatus(updateEvent.argList[0]) elif updateEvent.id == PSUGUIUpdateEvent.PSU_CONNECT_FAILED: self._onButton.button_type = "default" self._onButton.disabled = False self._setButton.disabled = True self._setButton.button_type = "default" self.statusBarWrapper.setStatus(updateEvent.argList[0]) elif updateEvent.id == PSUGUIUpdateEvent.CONNECTED_TO_PSU: self._setButton.button_type = "success" self._setButton.disabled = False self._onButton.button_type = "success" self._onButton.disabled = False self._onButton.label = "Off" self.statusBarWrapper.setStatus("PSU ON") self._pconfig.addAttr(PSUGUI.VOLTS, self.outputVoltageSpinner.value) self._pconfig.addAttr(PSUGUI.AMPS, self.currentLimitSpinner.value) self._pconfig.addAttr(PSUGUI.PLOT_SECONDS, self.plotHistorySpinner.value) self._pconfig.store() elif updateEvent.id == PSUGUIUpdateEvent.TURNING_PSU_OFF: self._onButton.button_type = "default" self._setButton.disabled = True self._onButton.disabled = True self._setButton.button_type = "default" self.statusBarWrapper.setStatus("Turning PSU OFF") elif updateEvent.id == PSUGUIUpdateEvent.PSU_OFF: self._onButton.button_type = "default" self._onButton.disabled = False self._onButton.label = "On" self.statusBarWrapper.setStatus("PSU OFF") elif updateEvent.id == PSUGUIUpdateEvent.SET_PSU_STATE: volts = self.outputVoltageSpinner.value amps = self.currentLimitSpinner.value self._pconfig.addAttr(PSUGUI.VOLTS, volts) self._pconfig.addAttr(PSUGUI.AMPS, amps) self._pconfig.addAttr(PSUGUI.PLOT_SECONDS, self.plotHistorySpinner.value) self._pconfig.store() self.statusBarWrapper.setStatus( "Set {:.2f} volts with a {:.3f} amp current limit".format( volts, amps)) elif updateEvent.id == PSUGUIUpdateEvent.SHUTDOWN_SERVER: self.stopServer() print("PJA: SERVER SHUTDOWN.") def _powerOnOff(self): """@brief Called to turn the PSU on/off""" if self._on: self._psuOff() else: self._psuOn() def _setPSU(self): """@brief Called when the PSU is on to set the state of the PSU.""" volts = self.outputVoltageSpinner.value amps = self.currentLimitSpinner.value self._psu.setVoltage(volts) self._psu.setCurrentLimit(amps) self._sendUpdateEvent(UpdateEvent(PSUGUIUpdateEvent.SET_PSU_STATE)) def getSelectedSerialPort(self): """@brief Get the selected serial port. @return the selected Serial port or None if not selected.""" selectedSerialPort = None if len(self.selectSerialPort.options) == 1: selectedSerialPort = self.selectSerialPort.options[0] elif self.selectSerialPort.value: selectedSerialPort = self.selectSerialPort.value if not selectedSerialPort and len(self.selectSerialPort.options) > 0: selectedSerialPort = self.selectSerialPort.options[0] return selectedSerialPort def _psuOff(self): """@brief Turn the PSU off.""" self._sendUpdateEvent(UpdateEvent(PSUGUIUpdateEvent.TURNING_PSU_OFF)) self._on = False self._psu.setOutput(False) self._psu.disconnect() self._psu = None self._sendUpdateEvent(UpdateEvent(PSUGUIUpdateEvent.PSU_OFF)) def _psuOn(self): """@brief Connect to the PDU. @return True if successfully connected to a PSU.""" serialPort = None try: serialPort = self.getSelectedSerialPort() if serialPort: self._sendUpdateEvent( UpdateEvent(PSUGUIUpdateEvent.CONNECTING_TO_PSU, ("Connecting to {}".format(serialPort), ))) self._psu = ETMXXXXP(serialPort) self._psu.connect() self._psu.getOutput() #Ensure the voltage comes up from a low voltage rather than down #from a previously higher voltage self._psu.setVoltage(0) self._psu.setVoltage(self.outputVoltageSpinner.value) self._psu.setCurrentLimit(self.currentLimitSpinner.value) self._psu.setOutput(True) self._on = True self._sendUpdateEvent( UpdateEvent(PSUGUIUpdateEvent.CONNECTED_TO_PSU)) else: self._sendUpdateEvent( UpdateEvent(PSUGUIUpdateEvent.PSU_CONNECT_FAILED, ( "Failed to to connect to PSU as no serial port was selected.", ))) except Exception as ex: print(ex) if self._psu: self._psu.disconnect() self._psu = None if serialPort: self._sendUpdateEvent( UpdateEvent( PSUGUIUpdateEvent.PSU_CONNECT_FAILED, ("Failed to connect to PSU on {}".format(serialPort), ))) else: self._sendUpdateEvent( UpdateEvent( PSUGUIUpdateEvent.PSU_CONNECT_FAILED, ("Failed to connect to PSU on {}".format(serialPort), ))) self.setStatus("Failed to connect to PSU.")
options=[x[0] for x in collections.Counter(ptree.leaf_cds.data['genus']).most_common()],\ width=200,height=70) gms.on_change('value',lambda attr,old,new:gmsfunc()) kms=MultiSelect(title='Kingdom',\ options=[x[0] for x in collections.Counter(ptree.leaf_cds.data['superkingdom']).most_common()],\ width=200,height=70) kms.on_change('value',lambda attr,old,new:kmsfunc()) sfms=MultiSelect(title='Subfams',\ options=[x[0] for x in collections.Counter(ptree.leaf_cds.data['subfamstr']).most_common()],\ width=150,height=70) sfms.on_change('value',lambda attr,old,new:sfmsfunc()) tcb=Button(label='CalcTC',width=80,height=40) tcb.on_click(tcbfunc) #splist=list(set(ptree.leaf_cds.data['species'])) #splist.sorted(key=ptree.leaf_cds.data['species']) #acw=AutocompleteInput(title='Organism name',completions=list(set(ptree.leaf_cds.data['species'])),width=200,height=50) acw=AutocompleteInput(title='Organism name',\ completions=[x[0] for x in collections.Counter(ptree.leaf_cds.data['species']).most_common()],\ width=200,height=50) acw.on_change('value',lambda attr,old,new:acfunc()) accacw=AutocompleteInput(title='Accession',\ completions=ptree.leaf_cds.data['gbacc'][:],width=200,height=50) accacw.on_change('value',lambda attr,old,new:accacfunc()) if len(sys.argv)>3: preselfpath=os.path.join(os.environ['SCIENCEDIR'],'GHSeqs',ghfam.upper(),sys.argv[3])
## there is a bug in bokeh image_rgba to be used later that requires the following flipping ## https://github.com/bokeh/bokeh/issues/1666 logo = logo[::-1] plogo = figure(x_range=(0, 25), y_range=(0, 15), tools=[], plot_width=333, plot_height=250) plogo.xgrid.grid_line_color = None plogo.ygrid.grid_line_color = None plogo.image_rgba(image=[logo], x=[0], y=[0], dw=[25], dh=[15]) plogo.xaxis.visible = False plogo.yaxis.visible = False div1 = Div( text= """<font size=6><b><h>EuroLeague in a Spreadsheet</b></h></font></br></br><font size=5>With COVID-19 hitting sports hard - just as with everything else - spreadsheets finally get the position they deserve in sports. Euroleague was suspended before the regular season ended. Simulate the rest of the regular season and the playoffs to get your sports fix <br><br>Basketball Guru (@wiseballsguru), K. Pelechrinis (@kpelechrinis). </font><br></br></br><br></br></br>""", width=800, height=250) button = Button(label="Simulate", button_type="success", width=250) button.on_click(sim_callback) div2 = Div(text="""""", width=350) div3 = Div(text="""""", width=100) #layout = layout([[div1,],[button,],[table_standings,table_playoffs]]) layout = (column(row(div1, div3, plogo), row(button), row(table_standings, div2, table_playoffs))) curdoc().add_root(layout)
plot.children[1] = column(create_plots(Estimators)) #lowecase innerscope variables variable1, variable2 = Variable1, Variable2 #Create the widgets drop1 = Select(title="Variable 1", options=list(X.columns.values), value=variable1) drop2 = Select(title="Variable 2", options=list(X.columns.values), value=variable2) drop3 = Select(title="variable 3", options=list(X.columns.values), value=y) estimator_names = [str(estimator).split("(")[0] for estimator in Estimators] estimator_indices = [str(i) for i, name in enumerate(estimator_names)] estimator_select = MultiSelect(title="Estimators", options=estimator_names, value=estimator_names[0:5]) button = Button(label="Update", button_type="success") button.on_click(update) plot = row(widgetbox(drop1, drop2, drop3, estimator_select, button), column(create_plots(Estimators))) curdoc().add_root(plot) curdoc().title = "Wage Data Visualisation"