Dropdown(label="Primary Dropdown 2", button_type="primary", menu=menu), Dropdown(label="Success Dropdown 3", button_type="success", menu=menu), CheckboxButtonGroup( labels=["Checkbox Option 1", "Checkbox Option 2", "Checkbox Option 3"], button_type="default", active=[0, 1]), CheckboxButtonGroup( labels=["Checkbox Option 4", "Checkbox Option 5", "Checkbox Option 6"], button_type="primary", active=[1, 2]), CheckboxButtonGroup( labels=["Checkbox Option 7", "Checkbox Option 8", "Checkbox Option 9"], button_type="success", active=[0, 2]), RadioButtonGroup( labels=["Radio Option 1", "Radio Option 2", "Radio Option 3"], button_type="default", active=0), RadioButtonGroup( labels=["Radio Option 4", "Radio Option 5", "Radio Option 6"], button_type="primary", active=1), RadioButtonGroup( labels=["Radio Option 7", "Radio Option 8", "Radio Option 9"], button_type="success", active=2), TextInput(placeholder="TextInput 1"), TextInput(placeholder="TextInput 2"), TextInput(placeholder="TextInput 3"), AutocompleteInput(placeholder="AutocompleteInput 1 ...", completions=["aaa", "aab", "aac", "baa", "caa"]), AutocompleteInput(placeholder="AutocompleteInput 2 ...",
"F", "D-", "D", "D+", "C-", "C", "C+", "B-", "B", "B+", "A-", "A", "A+" ]) #add labels for glyphs labels = LabelSet(x="average_grades", y="exam_grades", text="student_names", x_offset=5, y_offset=5, source=source) f.add_layout(labels) #create glyphs f.circle(x="average_grades", y="exam_grades", source=source, size=8) def update_labels(attr, old, new): labels.text = options[radio_button_group.active] #create radio buttons widget options = ["average_grades", "exam_grades", "student_names"] radio_button_group = RadioButtonGroup(labels=options) radio_button_group.on_change("active", update_labels) #create layout and add to curdoc lay_out = layout([[radio_button_group]]) curdoc().add_root(f) curdoc().add_root(lay_out)
def app(doc, hist_storage_, data_storage_, freq_storage_, depolarizer, names): # вспомогательные глобальные data_source = ColumnDataSource({key: [] for key in names}) fit_handler = { "fit_line": None, "input_fields": {}, "fit_indices": tuple() } utc_plus_7h = 7 * 3600 time_coef = 10**3 # Пересчёт времени в мс для формата datetime Bokeh fit_line_points_amount = 300 # Количество точек для отрисовки подгоночной кривой depol_list = [] datetime_formatter = DatetimeTickFormatter( milliseconds=['%M:%S:%3Nms'], seconds=['%H:%M:%S'], minsec=['%H:%M:%S'], minutes=['%H:%M:%S'], hourmin=['%H:%M:%S'], hours=['%H:%M:%S'], days=["%d.%m"], months=["%Y-%m-%d"], ) # Гистограмма пятна img, img_x_std, img_y_std = hist_storage_.get_hist_with_std() hist_source = ColumnDataSource(data=dict(image=[img])) width_ = config.GEM_X * 5 hist_height_ = config.GEM_Y * 5 hist_fig = figure(plot_width=width_, plot_height=hist_height_, x_range=(0, config.GEM_X), y_range=(0, config.GEM_Y)) hist_fig.image(image='image', x=0, y=0, dw=config.GEM_X, dh=config.GEM_Y, palette="Spectral11", source=hist_source) hist_label = Label( x=0, y=0, x_units='screen', y_units='screen', text=f"x_std={'%.2f' % img_x_std},y_std={'%.2f' % img_y_std}", render_mode='css', border_line_color='black', border_line_alpha=1.0, background_fill_color='white', background_fill_alpha=1.0) hist_fig.add_layout(hist_label) hist_buffer_len = config.hist_buffer_len - 1 hist_slider = RangeSlider(start=0, end=hist_buffer_len, value=(0, hist_buffer_len), step=1, title="Срез пятна (от..до) сек назад") def hist_update(): img, img_x_std, img_y_std = hist_storage_.get_hist_with_std( hist_buffer_len - hist_slider.value[1], hist_buffer_len - hist_slider.value[0]) hist_label.text = f"x_std={'%.2f' % img_x_std},y_std={'%.2f' % img_y_std}" hist_source.data = {'image': [img]} # График асимметрии asym_fig = figure( plot_width=width_, plot_height=400, tools="box_zoom, xbox_select, wheel_zoom, pan, save, reset", active_scroll="wheel_zoom", active_drag="pan", toolbar_location="below", lod_threshold=100, x_axis_location=None, x_range=DataRange1d()) asym_fig.yaxis.axis_label = "мм" asym_fig.extra_x_ranges = { "time_range": asym_fig.x_range, "depolarizer": asym_fig.x_range, "sec": asym_fig.x_range } depol_axis = LinearAxis(x_range_name="depolarizer", axis_label='Деполяризатор', major_label_overrides={}, major_label_orientation=pi / 2) asym_fig.add_layout( LinearAxis(x_range_name="time_range", axis_label='Время', formatter=datetime_formatter), 'below') # Прямая, с которой идёт отсчёт времени для подгонки zone_of_interest = Span(location=0, dimension='height', line_color='green', line_dash='dashed', line_width=3) sec_axis = LinearAxis( x_range_name='sec', axis_label='Секунды') # Секундная ось сверху (настр. диапазон) sec_axis.formatter = FuncTickFormatter( code= f"return ((tick - {zone_of_interest.location}) / {time_coef}).toFixed(1);" ) def double_tap(event): """Двойной клик для перемещения отсчёта времени для подгонки""" zone_of_interest.location = event.x sec_axis.formatter = FuncTickFormatter( code=f"return ((tick - {event.x}) / {time_coef}).toFixed(1);") asym_fig.add_layout(depol_axis, 'below') asym_fig.add_layout(sec_axis, 'above') asym_fig.add_layout(zone_of_interest) asym_fig.on_event(DoubleTap, double_tap) def draw_selected_area(attr, old, new): """Подсветка выделенной для подгонки области""" # Удаляет предыдущую выделенную область asym_fig.renderers = [ r for r in asym_fig.renderers if r.name != 'fit_zone' ] if not new.indices: fit_handler["fit_indices"] = tuple() return l_time_ = data_source.data['time'][min(new.indices)] r_time_ = data_source.data['time'][max(new.indices)] if l_time_ != r_time_: fit_handler["fit_indices"] = (l_time_, r_time_) box_select = BoxAnnotation(left=l_time_, right=r_time_, name="fit_zone", fill_alpha=0.1, fill_color='red') asym_fig.add_layout(box_select) asym_box_select_overlay = asym_fig.select_one(BoxSelectTool).overlay asym_box_select_overlay.line_color = "firebrick" data_source.on_change('selected', draw_selected_area) def create_whisker(data_name: str): """ Создает усы для data_name от time :param data_name: имя поля данных из data_storage (у данных должны быть поля '_up_error', '_down_error') :return: Bokeh Whisker """ return Whisker(source=data_source, base="time", upper=data_name + "_up_error", lower=data_name + "_down_error") def create_render(data_name: str, glyph: str, color: str): """ Рисует data_name от time :param data_name: имя поля данных из data_storage :param glyph: ['circle', 'square'] :param color: цвет :return: Bokeh fig """ if glyph == 'circle': func = asym_fig.circle elif glyph == 'square': func = asym_fig.square else: raise ValueError('Неверное значение glyph') return func('time', data_name, source=data_source, name=data_name, color=color, nonselection_alpha=1, nonselection_color=color) # Список линий на графике асимметрии: data_name, name, glyph, color asym_renders_name = [('y_one_asym', 'ΔY ONE', 'circle', 'black'), ('y_cog_asym', 'ΔY COG', 'circle', 'green'), ('x_one_asym', 'ΔX ONE', 'square', 'black'), ('x_cog_asym', 'ΔX COG', 'square', 'green')] pretty_names = dict([(data_name, name) for data_name, name, *_ in asym_renders_name]) asym_renders = [ create_render(data_name, glyph, color) for data_name, _, glyph, color in asym_renders_name ] asym_error_renders = [ create_whisker(data_name) for data_name, *_ in asym_renders_name ] for render, render_error in zip(asym_renders, asym_error_renders): asym_fig.add_layout(render_error) render.js_on_change( 'visible', CustomJS(args=dict(x=render_error), code="x.visible = cb_obj.visible")) asym_fig.add_layout( Legend(items=[(pretty_names[r.name], [r]) for r in asym_renders], click_policy="hide", location="top_left", background_fill_alpha=0.2, orientation="horizontal")) # Вывод информации о точке при наведении мыши asym_fig.add_tools( HoverTool( renderers=asym_renders, formatters={"time": "datetime"}, mode='vline', tooltips=[ ("Время", "@time{%F %T}"), *[(pretty_names[r.name], f"@{r.name}{'{0.000}'} ± @{r.name + '_error'}{'{0.000}'}") for r in asym_renders], ("Деполяризатор", f"@depol_energy{'{0.000}'}") ])) # Окно ввода периода усреднения period_input = TextInput(value='300', title="Время усреднения (с):") # Глобальный список параметров, для сохранения результатов запросов к data_storage params = {'last_time': 0, 'period': int(period_input.value)} def update_data(): """ Обновляет данные для пользовательского интерфейса, собирая их у data_storage """ if params['period'] != int(period_input.value): data_source.data = {name: [] for name in names} params['period'] = int(period_input.value) params['last_time'] = 0 depol_axis.ticker = [] depol_axis.major_label_overrides.clear() depol_list.clear() points, params['last_time'] = data_storage_.get_mean_from( params['last_time'], params['period']) if not points['time']: return points['time'] = [(i + utc_plus_7h) * time_coef for i in points['time'] ] # Учёт сдвижки UTC+7 для отрисовки for time, energy in zip(points['time'], points['depol_energy']): if energy == 0: continue depol_axis.major_label_overrides[time] = str(energy) depol_list.append(time) depol_axis.ticker = depol_list # TODO: оптимизировать data_source.stream({key: np.array(val) for key, val in points.items()}, rollover=250) def period_correction_func(attr, old, new): """Проверка введенного значения на целое число больше нуля""" if not new.isdigit() or int(new) <= 0: period_input.value = old period_input.on_change('value', period_correction_func) # Создание панели графиков (вкладок) def create_fig(data_names: list, colors: list, y_axis_name: str, ers: str = None): """Создаёт график data_names : time. Если в data_names несколько имён, то они будут на одном графике. Возвращает fig. :param data_names: список с именами полей данных из data_storage :param colors: список цветов, соотв. элементам из fig_names :param y_axis_name: имя оси Y :param ers: 'err', 'pretty' --- вид усов (у данных должны быть поля '_up_error', '_down_error'), 'err' --- усы обыкновенные 'pretty' --- усы без шляпки и цветом совпадающим с цветом точки :return fig --- Bokeh figure """ if len(data_names) != len(colors): raise IndexError('Кол-во цветов и графиков не совпадает') fig = figure(plot_width=width_, plot_height=300, tools="box_zoom, wheel_zoom, pan, save, reset", active_scroll="wheel_zoom", lod_threshold=100, x_axis_type="datetime") for fig_name, color in zip(data_names, colors): if ers == 'err': fig.add_layout( Whisker(source=data_source, base="time", upper=fig_name + '_up_error', lower=fig_name + '_down_error')) elif ers == 'pretty': fig.add_layout( Whisker(source=data_source, base="time", upper=fig_name + '_up_error', lower=fig_name + '_down_error', line_color=color, lower_head=None, upper_head=None)) fig.circle('time', fig_name, source=data_source, size=5, color=color, nonselection_alpha=1, nonselection_color=color) fig.yaxis.axis_label = y_axis_name fig.xaxis.axis_label = 'Время' fig.xaxis.formatter = datetime_formatter fig.x_range = asym_fig.x_range return fig figs = [(create_fig(['y_one_l'], ['black'], 'Y [мм]', 'err'), 'Y ONE L'), (create_fig(['y_one_r'], ['black'], 'Y [мм]', 'err'), 'Y ONE R'), (create_fig(['y_cog_l'], ['black'], 'Y [мм]', 'err'), 'Y COG L'), (create_fig(['y_cog_r'], ['black'], 'Y [мм]', 'err'), 'Y COG R'), (create_fig(['rate' + i for i in ['_l', '_r']], ['red', 'blue'], 'Усл. ед.', 'pretty'), 'Rate'), (create_fig(['corrected_rate' + i for i in ['_l', '_r']], ['red', 'blue'], 'Усл. ед.', 'pretty'), 'Corr. rate'), (create_fig(['delta_rate'], ['black'], 'Корр. лев. - корр. пр.', 'err'), 'Delta corr. rate'), (create_fig(['charge'], ['blue'], 'Ед.'), 'Charge')] tab_handler = Tabs( tabs=[Panel(child=fig, title=fig_name) for fig, fig_name in figs], width=width_) # Окно статуса деполяризатора depol_status_window = Div(text="Инициализация...", width=500, height=500) depol_start_stop_buttons = RadioButtonGroup( labels=["Старт", "Стоп"], active=(0 if depolarizer.is_scan else 1)) fake_depol_button = Button(label="Деполяризовать", width=200) fake_depol_button.on_click(GEM.depolarize) depol_input_harmonic_number = TextInput(value=str( '%.1f' % depolarizer.harmonic_number), title=f"Номер гармоники", width=150) depol_input_attenuation = TextInput(value=str('%.1f' % depolarizer.attenuation), title=f"Аттенюатор (дБ)", width=150) depol_input_speed = TextInput( value=str(depolarizer.frequency_to_energy(depolarizer.speed, n=0)), title=f"Скорость ({'%.1f' % depolarizer.speed} Гц):", width=150) depol_input_step = TextInput( value=str(depolarizer.frequency_to_energy(depolarizer.step, n=0)), title=f"Шаг ({'%.1f' % depolarizer.step} Гц):", width=150) depol_input_initial = TextInput( value=str(depolarizer.frequency_to_energy(depolarizer.initial)), title=f"Начало ({'%.1f' % depolarizer.initial} Гц):", width=150) depol_input_final = TextInput( value=str(depolarizer.frequency_to_energy(depolarizer.final)), title=f"Конец ({'%.1f' % depolarizer.final} Гц):", width=150) depol_dict = { "speed": (depol_input_speed, depolarizer.set_speed), "step": (depol_input_step, depolarizer.set_step), "initial": (depol_input_initial, depolarizer.set_initial), "final": (depol_input_final, depolarizer.set_final), "harmonic_number": (depol_input_harmonic_number, depolarizer.set_harmonic_number), "attenuation": (depol_input_attenuation, depolarizer.set_attenuation) } def change_value_generator(value_name): """Возвращает callback функцию для параметра value_name деполяризатора""" def change_value(attr, old, new): if float(old) == float(new): return depol_input, depol_set = depol_dict[value_name] depol_current = depolarizer.get_by_name(value_name) try: if value_name in ['harmonic_number', 'attenuation']: new_val = float(new) elif value_name in ['speed', 'step']: new_val = depolarizer.energy_to_frequency(float(new), n=0) else: new_val = depolarizer.energy_to_frequency(float(new)) if depol_current == new_val: return depol_set(new_val) if value_name not in ['harmonic_number', 'attenuation']: name = depol_input.title.split(' ')[0] depol_input.title = name + f" ({'%.1f' % new_val} Гц):" except ValueError as e: if value_name in ['harmonic_number', 'attenuation']: depol_input.value = str(depol_current) elif value_name in ['speed', 'step']: depol_input.value = str( depolarizer.frequency_to_energy(depol_current, n=0)) else: depol_input.value = str( depolarizer.frequency_to_energy(depol_current)) print(e) return change_value depol_input_harmonic_number.on_change( 'value', change_value_generator('harmonic_number')) depol_input_attenuation.on_change('value', change_value_generator("attenuation")) depol_input_speed.on_change('value', change_value_generator("speed")) depol_input_step.on_change('value', change_value_generator("step")) depol_input_initial.on_change('value', change_value_generator("initial")) depol_input_final.on_change('value', change_value_generator("final")) def update_depol_status( ): # TODO: самому пересчитывать начало и конец сканирования по частотам """Обновляет статус деполяризатора, если какое-то значение поменялось другим пользователем""" depol_start_stop_buttons.active = 0 if depolarizer.is_scan else 1 depol_status_window.text = f""" <p>Сканирование: <font color={'"green">включено' if depolarizer.is_scan else '"red">выключено'}</font></p> <p/>Частота {"%.1f" % depolarizer.current_frequency} (Гц)</p> <p/>Энергия {"%.3f" % depolarizer.current_energy} МэВ</p>""" for value_name in ['speed', 'step']: depol_input, _ = depol_dict[value_name] depol_value = depolarizer.frequency_to_energy( depolarizer.get_by_name(value_name), n=0) if float(depol_input.value) != depol_value: depol_input.value = str(depol_value) for value_name in ['initial', 'final']: depol_input, _ = depol_dict[value_name] freq = depolarizer.get_by_name(value_name) energy = depolarizer.frequency_to_energy(freq) if float(depol_input.value) != energy: depol_input.value = str(energy) else: name = depol_input.title.split(' ')[0] depol_input.title = name + f" ({'%.1f' % freq} Гц):" for value_name in ['attenuation', 'harmonic_number']: depol_input, _ = depol_dict[value_name] depol_value = depolarizer.get_by_name(value_name) if float(depol_input.value) != depol_value: depol_input.value = str(int(depol_value)) depol_start_stop_buttons.on_change( "active", lambda attr, old, new: (depolarizer.start_scan() if new == 0 else depolarizer.stop_scan())) # Подгонка fit_line_selection_widget = Select(title="Fitting line:", width=200, value=asym_renders[0].name, options=[(render.name, pretty_names[render.name]) for render in asym_renders]) options = [name for name in fit.function_handler.keys()] if not options: raise IndexError("Пустой function_handler в fit.py") fit_function_selection_widget = Select(title="Fitting function:", value=options[0], options=options, width=200) fit_button = Button(label="FIT", width=200) def make_parameters_table(): """Создание поля ввода данных для подгонки: начальное значение, fix и т.д.""" name = fit_function_selection_widget.value t_width = 10 t_height = 12 rows = [ row(Paragraph(text="name", width=t_width, height=t_height), Paragraph(text="Fix", width=t_width, height=t_height), Paragraph(text="Init value", width=t_width, height=t_height), Paragraph(text="step (error)", width=t_width, height=t_height), Paragraph(text="limits", width=t_width, height=t_height), Paragraph(text="lower_limit", width=t_width, height=t_height), Paragraph(text="upper_limit", width=t_width, height=t_height)) ] fit_handler["input_fields"] = {} for param, value in fit.get_function_params(name): fit_handler["input_fields"][param] = {} fit_handler["input_fields"][param]["fix"] = CheckboxGroup( labels=[""], width=t_width, height=t_height) fit_handler["input_fields"][param]["Init value"] = TextInput( width=t_width, height=t_height, value=str(value)) fit_handler["input_fields"][param]["step (error)"] = TextInput( width=t_width, height=t_height, value='1') fit_handler["input_fields"][param]["limits"] = CheckboxGroup( labels=[""], width=t_width, height=t_height) fit_handler["input_fields"][param]["lower_limit"] = TextInput( width=t_width, height=t_height) fit_handler["input_fields"][param]["upper_limit"] = TextInput( width=t_width, height=t_height) rows.append( row(Paragraph(text=param, width=t_width, height=t_height), fit_handler["input_fields"][param]["fix"], fit_handler["input_fields"][param]["Init value"], fit_handler["input_fields"][param]["step (error)"], fit_handler["input_fields"][param]["limits"], fit_handler["input_fields"][param]["lower_limit"], fit_handler["input_fields"][param]["upper_limit"])) return column(rows) def clear_fit(): """Удаление подогнанной кривой""" if fit_handler["fit_line"] in asym_fig.renderers: asym_fig.renderers.remove(fit_handler["fit_line"]) energy_window = Div(text="Частота: , энергия: ") clear_fit_button = Button(label="Clear", width=200) clear_fit_button.on_click(clear_fit) def fit_callback(): if not fit_handler["fit_indices"]: return name = fit_function_selection_widget.value line_name = fit_line_selection_widget.value left_time_, right_time_ = fit_handler["fit_indices"] left_ind_ = bisect.bisect_left(data_source.data['time'], left_time_) right_ind_ = bisect.bisect_right(data_source.data['time'], right_time_, lo=left_ind_) if left_ind_ == right_ind_: return clear_fit() x_axis = data_source.data['time'][left_ind_:right_ind_] y_axis = data_source.data[line_name][left_ind_:right_ind_] y_errors = data_source.data[line_name + '_up_error'][left_ind_:right_ind_] - y_axis init_vals = { name: float(val["Init value"].value) for name, val in fit_handler["input_fields"].items() } steps = { "error_" + name: float(val["step (error)"].value) for name, val in fit_handler["input_fields"].items() } fix_vals = { "fix_" + name: True for name, val in fit_handler["input_fields"].items() if val["fix"].active } limit_vals = { "limit_" + name: (float(val["lower_limit"].value), float(val["upper_limit"].value)) for name, val in fit_handler["input_fields"].items() if val["limits"].active } kwargs = {} kwargs.update(init_vals) kwargs.update(steps) kwargs.update(fix_vals) kwargs.update(limit_vals) # Предобработка времени, перевод в секунды, вычитание сдвига (для лучшей подгонки) left_ = zone_of_interest.location x_time = x_axis - left_ # Привёл время в интервал от 0 x_time /= time_coef # Перевёл в секунды # Создание точек, которые передадутся в подогнанную функцию с параметрами, # и точек, которые соответсвуют реальным временам на графике (т.е. без смещения к 0) fit_line_real_x_axis = np.linspace(left_time_, right_time_, fit_line_points_amount) fit_line_x_axis = fit_line_real_x_axis - left_ fit_line_x_axis /= time_coef m = fit.create_fit_func(name, x_time, y_axis, y_errors, kwargs) fit.fit(m) params_ = m.get_param_states() for param in params_: fit_handler["input_fields"][ param['name']]["Init value"].value = "%.3f" % param['value'] fit_handler["input_fields"][ param['name']]["step (error)"].value = "%.3f" % param['error'] if param['name'] == "depol_time": freq = freq_storage_.find_closest_freq(param['value'] + left_ / time_coef - utc_plus_7h) freq_error = abs(depolarizer.speed * param['error']) energy = depolarizer.frequency_to_energy( freq) if freq != 0 else 0 energy_error = depolarizer.frequency_to_energy( freq_error, depolarizer._F0, 0) energy_window.text = "<p>Частота: %8.1f +- %.1f Hz,</p> <p>Энергия: %7.3f +- %.1f МэВ</p>" % ( freq, freq_error, energy, energy_error) fit_handler["fit_line"] = asym_fig.line( fit_line_real_x_axis, fit.get_line(name, fit_line_x_axis, [x['value'] for x in params_]), color="red", line_width=2) fit_button.on_click(fit_callback) # Инициализация bokeh app, расположение виджетов column_1 = column(gridplot([tab_handler], [asym_fig], merge_tools=False), period_input, width=width_ + 50) widgets_ = WidgetBox(depol_start_stop_buttons, depol_input_harmonic_number, depol_input_attenuation, depol_input_speed, depol_input_step, depol_input_initial, depol_input_final, depol_status_window) row_21 = column(hist_fig, hist_slider) column_21 = column(widgets_) if config.GEM_idle: column_22 = column(fit_button, clear_fit_button, fake_depol_button, fit_line_selection_widget, fit_function_selection_widget, energy_window, make_parameters_table()) make_parameters_table_id = 6 else: column_22 = column(fit_button, clear_fit_button, fit_line_selection_widget, fit_function_selection_widget, energy_window, make_parameters_table()) make_parameters_table_id = 5 def rebuild_table(attr, old, new): column_22.children[make_parameters_table_id] = make_parameters_table() fit_function_selection_widget.on_change("value", rebuild_table) row_22 = row(column_21, column_22) column_2 = column(row_21, row_22, width=width_ + 50) layout_ = layout([[column_1, column_2]]) # Настройка документа Bokeh update_data() doc.add_periodic_callback(hist_update, 1000) # TODO запихнуть в один callback doc.add_periodic_callback(update_data, 1000) # TODO: подобрать периоды doc.add_periodic_callback(update_depol_status, 1000) doc.title = "Laser polarimeter" doc.add_root(layout_)
end=99.0, step=0.1) # for labels (filters): lbl_ops = ['None', '-', '+', '*', '/'] filters_optional = ['None'] filters = [] for afilter in isocmds[0.00][0.0][0.0].hdr_list[9:-1]: filters.append(afilter) filters_optional.append(afilter) # axis value and operator selection # x-axis: x_lbl1 = Select(title="x-axis value 1", value=v_label, options=filters) x_op_title = Div(text="""x-axis operator (optional)""", height=10, width=200) x_op = RadioButtonGroup(active=1, labels=lbl_ops) x_lbl2 = Select(title="x-axis value 2 (optional)", value=i_label, options=filters_optional) #y-axis: y_lbl1 = Select(title="y-axis value 1", value=i_label, options=filters) y_op_title = Div(text="""y-axis operator (optional)""", height=10, width=200) y_op = RadioButtonGroup(active=0, labels=lbl_ops) y_lbl2 = Select(title="y-axis value 2 (optional)", value='None', options=filters_optional) # initial mass slider: mi_slider = RangeSlider(start=mi_range[0], end=mi_range[-1], value=(0.1, 8.0),
def source_comparison_tab(old_to_new_sources, new_to_old_sources, old_to_new_regions, n_folds_list, agg_all_nfolds): ## Get region names and list of sources region_names_new = list(old_to_new_regions.values()) region_names_new.sort() # sourceset_display = ['Best'] + list(old_to_new_sources.keys())[1:] # sourceset_display = list(old_to_new_sources.keys())[1:] sourceset_display = sorted(list(new_to_old_sources.keys())[1:]) # Put Climate at the end if 'Climate' in sourceset_display: c_idx = sourceset_display.index('Climate') sourceset_display.pop(c_idx) sourceset_display.append('Climate') n_folds_display = [np.str(x) for x in n_folds_list] # Replace long names to shorter ones for display shorter_source_names = { 'Colombia & GT by State': 'CO & GT by State', 'Colombia & GT by Symptom': 'CO & GT by Symptom', 'Colombia Border & GT': 'CO Border & GT', 'GT by State - Ven & Col': 'GT by State - VE & CO' } sourceset_display_short = [ x if x not in shorter_source_names.keys() else shorter_source_names[x] for x in sourceset_display ] # All regions and sources # all_regions = np.unique(agg_all_nfolds.Region).tolist() # all_sources = np.unique(agg_all_nfolds.SourcesSet).tolist() # Score types considered score_types = ['In Sample', 'OOS'] # Data to display: select one region and one value of n_folds gs_name_bar = 'Amazonas' n_folds_bar = n_folds_list[0] agg_gs = agg_all_nfolds.loc[agg_all_nfolds.Region == gs_name_bar] agg_n_gs = agg_gs.loc[agg_gs.NbFolds == n_folds_bar,:].\ sort_values(by='SourcesSet',ascending=True) # agg_n_gs.sort_values(by='SourcesSet',ascending=True,inplace=True) in_sample_vals = agg_n_gs.loc[agg_n_gs.ScoreType == 'InSample',\ 'Value'].values.tolist() OOS_vals = agg_n_gs.loc[agg_n_gs.ScoreType == 'OOS',\ 'Value'].values.tolist() data_dict = { 'Source Set': sourceset_display_short, 'In Sample': in_sample_vals, 'OOS': OOS_vals } x = [(s, score) for s in sourceset_display_short for score in score_types] counts = sum(zip(data_dict['In Sample'], data_dict['OOS']), ()) # like an hstack colors = (Category10[3])[1:] * len(sourceset_display_short) source_bar = ColumnDataSource(data=dict(x=x, counts=counts, color=colors)) p_bar = figure(x_range=FactorRange(*x), plot_height=350, plot_width=1050, title='R squared for a given Region', toolbar_location=None, tools="") p_bar.vbar(x='x', top='counts', width=0.9, source=source_bar, color='color') p_bar.y_range.start = -1 p_bar.y_range.end = 1 p_bar.x_range.range_padding = 0.1 p_bar.xaxis.major_label_orientation = 1 p_bar.xgrid.grid_line_color = None #show(p_bar,browser="chrome") ## Radio buttons for bar graph # Define buttons regions_button_bar = RadioButtonGroup(labels=region_names_new, active=0) n_folds_button_bar = RadioButtonGroup(labels=n_folds_display, active=0) # Text above graph # div_text_bar = 'Gold Standard: ' + gs_name_bar + \ # '<br>Predictor Set: ' + np.str(n_folds_bar) div_text_bar = '' div_bar = Div(text=div_text_bar, width=700, height=50) # Update function ########################################################################### def bar_callback(attr, old, new): # Get new selected value #new_score = score_types[new.active] gs_name_bar = region_names_new[regions_button_bar.active] n_folds_bar = np.int(n_folds_display[n_folds_button_bar.active]) # Update data to display agg_gs = agg_all_nfolds.loc[agg_all_nfolds.Region == gs_name_bar] agg_n_gs = agg_gs.loc[agg_gs.NbFolds == n_folds_bar,:].\ sort_values(by='SourcesSet',ascending=True) # agg_n_gs.sort_values(by='SourcesSet',ascending=True,inplace=True) in_sample_vals = agg_n_gs.loc[agg_n_gs.ScoreType == 'InSample',\ 'Value'].values.tolist() OOS_vals = agg_n_gs.loc[agg_n_gs.ScoreType == 'OOS',\ 'Value'].values.tolist() data_dict = { 'Source Set': sourceset_display_short, 'In Sample': in_sample_vals, 'OOS': OOS_vals } # x = [ (s, score) for s in sourceset_display for score in score_types ] counts = sum(zip(data_dict['In Sample'], data_dict['OOS']), ()) # like an hstack # new_source = ColumnDataSource(data=dict(x=x, counts=counts, color=colors)) new_source_data = dict(x=x, counts=counts, color=colors) source_bar.data = new_source_data # new_text_bar = 'Gold Standard: ' + gs_name_bar + \ # '<br>Predictor Set: ' + np.str(n_folds_bar) # div_bar.text = new_text_bar return ########################################################################### regions_button_bar.on_change('active', bar_callback) n_folds_button_bar.on_change('active', bar_callback) # Put controls in a single element controls = WidgetBox(n_folds_button_bar, regions_button_bar) # Create a row layout layout = column(controls, div_bar, p_bar) # Make a tab with the layout tab = Panel(child=layout, title='Comparison of Source Sets') return tab ###############################################################################
def bokeh_plot(histo, jup_url="http://127.0.0.1:8889"): if not isnotebook(): raise NotImplementedError("Only usable in jupyter notebook") import bokeh.plotting.figure as bk_figure from bokeh.io import show from bokeh import palettes from bokeh.layouts import row, column from bokeh.models import ColumnDataSource from bokeh.models.widgets import RadioButtonGroup, CheckboxButtonGroup from bokeh.models.widgets import RangeSlider, Div from bokeh.io import output_notebook # enables plot interface in J notebook # init bokeh from bokeh.application import Application from bokeh.application.handlers import FunctionHandler from bokeh.core.validation import silence from bokeh.core.validation.warnings import EMPTY_LAYOUT silence(EMPTY_LAYOUT, True) output_notebook() # Set up widgets cfg_labels = ["Ghost"] wi_config = CheckboxButtonGroup(labels=cfg_labels, active=[0]) wi_dense_select = RadioButtonGroup( labels=[ax.name for ax in histo.dense_axes()], active=0) wi_sparse_select = RadioButtonGroup( labels=[ax.name for ax in histo.sparse_axes()], active=0) # Dense widgets sliders = {} for ax in histo.dense_axes(): edge_vals = (histo.axis(ax.name).edges()[0], histo.axis(ax.name).edges()[-1]) _smallest_bin = numpy.min(numpy.diff(histo.axis(ax.name).edges())) sliders[ax.name] = RangeSlider( title=ax.name, value=edge_vals, start=edge_vals[0], end=edge_vals[1], step=_smallest_bin, name=ax.name, ) # Cat widgets togglers = {} for ax in histo.sparse_axes(): togglers[ax.name] = CheckboxButtonGroup( labels=[i.name for i in ax.identifiers()], active=[0], name=ax.name) # Toggles for all widgets configers = {} for ax in histo.sparse_axes(): configers[ax.name] = CheckboxButtonGroup(labels=["Display", "Ghost"], active=[0, 1], name=ax.name) for ax in histo.dense_axes(): configers[ax.name] = CheckboxButtonGroup(labels=["Display"], active=[0], name=ax.name) # Figure fig = bk_figure( title="1D Projection", plot_width=500, plot_height=500, min_border=20, toolbar_location=None, ) fig.yaxis.axis_label = "N" fig.xaxis.axis_label = "Quantity" # Iterate over possible overlays _max_idents = 0 # Max number of simultaneou histograms for ax in histo.sparse_axes(): _max_idents = max(_max_idents, len([i.name for i in ax.identifiers()])) # Data source list sources = [] sources_ghost = [] for i in range(_max_idents): sources.append( ColumnDataSource(dict(left=[], top=[], right=[], bottom=[]))) sources_ghost.append( ColumnDataSource(dict(left=[], top=[], right=[], bottom=[]))) # Hist list hists = [] hists_ghost = [] for i in range(_max_idents): if _max_idents < 10: _color = palettes.Category10[min(max(3, _max_idents), 10)][i] else: _color = palettes.magma(_max_idents)[i] hists.append( fig.quad( left="left", right="right", top="top", bottom="bottom", source=sources[i], alpha=0.9, color=_color, )) hists_ghost.append( fig.quad( left="left", right="right", top="top", bottom="bottom", source=sources_ghost[i], alpha=0.05, color=_color, )) def update_data(attrname, old, new): sparse_active = wi_sparse_select.active sparse_name = [ax.name for ax in histo.sparse_axes()][sparse_active] sparse_other = [ ax.name for ax in histo.sparse_axes() if ax.name != sparse_name ] dense_active = wi_dense_select.active dense_name = [ax.name for ax in histo.dense_axes()][dense_active] dense_other = [ ax.name for ax in histo.dense_axes() if ax.name != dense_name ] # Apply cuts in projections _h = histo.copy() for proj_ax in sparse_other: _idents = histo.axis(proj_ax).identifiers() _labels = [ident.name for ident in _idents] if 0 in configers[proj_ax].active: _h = _h.integrate( proj_ax, [_labels[i] for i in togglers[proj_ax].active]) else: _h = _h.integrate(proj_ax) for proj_ax in dense_other: _h = _h.integrate( proj_ax, slice(sliders[proj_ax].value[0], sliders[proj_ax].value[1])) for cat_ix in range(_max_idents): # Update histo for each toggled overlay if cat_ix in togglers[sparse_name].active: cat_value = histo.axis(sparse_name).identifiers()[cat_ix] h1d = _h.integrate(sparse_name, cat_value) # Get shown histogram values = h1d.project(dense_name).values() if values != {}: h = values[()] bins = h1d.axis(dense_name).edges() # Apply cuts on shown axis bin_los = bins[:-1][ bins[:-1] > sliders[dense_name].value[0]] bin_his = bins[1:][bins[1:] < sliders[dense_name].value[1]] new_bins = numpy.intersect1d(bin_los, bin_his) bin_ixs = numpy.searchsorted(bins, new_bins)[:-1] h = h[bin_ixs] sources[cat_ix].data = dict( left=new_bins[:-1], right=new_bins[1:], top=h, bottom=numpy.zeros_like(h), ) else: sources[cat_ix].data = dict(left=[], right=[], top=[], bottom=[]) # Add ghosts if 0 in wi_config.active: h1d = histo.integrate(sparse_name, cat_value) for proj_ax in sparse_other: _idents = histo.axis(proj_ax).identifiers() _labels = [ident.name for ident in _idents] if 1 not in configers[proj_ax].active: h1d = h1d.integrate( proj_ax, [_labels[i] for i in togglers[proj_ax].active]) else: h1d = h1d.integrate(proj_ax) values = h1d.project(dense_name).values() if values != {}: h = h1d.project(dense_name).values()[()] bins = h1d.axis(dense_name).edges() sources_ghost[cat_ix].data = dict( left=bins[:-1], right=bins[1:], top=h, bottom=numpy.zeros_like(h), ) else: sources_ghost[cat_ix].data = dict(left=[], right=[], top=[], bottom=[]) else: sources[cat_ix].data = dict(left=[], right=[], top=[], bottom=[]) sources_ghost[cat_ix].data = dict(left=[], right=[], top=[], bottom=[]) # Cosmetics fig.xaxis.axis_label = dense_name for name, slider in sliders.items(): slider.on_change("value", update_data) for name, toggler in togglers.items(): toggler.on_change("active", update_data) for name, configer in configers.items(): configer.on_change("active", update_data) # Button for w in [wi_dense_select, wi_sparse_select, wi_config]: w.on_change("active", update_data) from bokeh.models.widgets import Panel, Tabs layout = row( fig, column( Div( text="<b>Overlay Axis:</b>", style={ "font-size": "100%", "color": "black" }, ), wi_sparse_select, Div(text="<b>Plot Axis:</b>", style={ "font-size": "100%", "color": "black" }), wi_dense_select, Div( text="<b>Categorical Cuts:</b>", style={ "font-size": "100%", "color": "black" }, ), *[toggler for name, toggler in togglers.items()], Div(text="<b>Dense Cuts:</b>", style={ "font-size": "100%", "color": "black" }), *[slider for name, slider in sliders.items()]), ) # Config prep incl_lists = [[], [], []] for i, key in enumerate(list(configers.keys())): incl_lists[i // max(5, len(list(configers.keys())) / 3)].append( Div( text="<b>{}:</b>".format(key), style={ "font-size": "70%", "color": "black" }, )) incl_lists[i // max(5, len(list(configers.keys())) / 3)].append( configers[key]) layout_cfgs = column( row( column( Div( text="<b>Configs:</b>", style={ "font-size": "100%", "color": "black" }, ), wi_config, )), Div(text="<b>Axis togglers:</b>", style={ "font-size": "100%", "color": "black" }), row( column(incl_lists[0]), column(incl_lists[1]), column(incl_lists[2]), ), ) # Update active buttons def update_layout(attrname, old, new): active_axes = [None] for name, wi in configers.items(): if 0 in wi.active: active_axes.append(name) for child in layout.children[1].children: if child.name not in active_axes: child.visible = False else: child.visible = True for name, configer in configers.items(): configer.on_change("active", update_layout) tab1 = Panel(child=layout, title="Projection") tab2 = Panel(child=layout_cfgs, title="Configs") tabs = Tabs(tabs=[tab1, tab2]) def modify_doc(doc): doc.add_root(row(tabs, width=800)) doc.title = "Sliders" handler = FunctionHandler(modify_doc) app = Application(handler) show(app, notebook_url=jup_url) update_data("", "", "")
msg = "No csv files chosen" MessageBox.text = msg return fpath = os.path.join(fdir, csvfiles[0]) cfg = bu.config() Config = Paragraph(text=bu.config()) Above = Paragraph(text="You are Here") Here = Paragraph() Here.text = CurDir MessageBox = Paragraph(text="Go") Timer = Paragraph() spacer = Paragraph(width=10) blabels = bu.list_from_path(CurDir) button_group = RadioButtonGroup(labels=blabels) button_group.on_click(dir_select) def dirs_source_callback(attr, old, new): global dirs_source oldind = old.get('1d', None) newind = (new['1d']['indices'][0] if len(new['1d']['indices']) > 0 else None) if newind is None: return if dirs_source.data['Name'][newind] == '.': return newpath = (dirs_source.data['Name'][newind] if newind is not None else None)
menu=menu, default_action="baz") split.on_click(split_handler) checkbox_group = CheckboxGroup(labels=["Option 1", "Option 2", "Option 3"], active=[0, 1]) checkbox_group.on_click(checkbox_group_handler) radio_group = RadioGroup(labels=["Option 1", "Option 2", "Option 3"], active=0) radio_group.on_click(radio_group_handler) checkbox_button_group = CheckboxButtonGroup( labels=["Option 1", "Option 2", "Option 3"], active=[0, 1]) checkbox_button_group.on_click(checkbox_button_group_handler) radio_button_group = RadioButtonGroup( labels=["Option 1", "Option 2", "Option 3"], active=0) radio_button_group.on_click(radio_button_group_handler) vbox = VBox(children=[ button, toggle, dropdown, split, checkbox_group, radio_group, checkbox_button_group, radio_button_group ]) document.add(vbox) session.store_document(document) if __name__ == "__main__": link = session.object_link(document.context) print("Please visit %s to see the plots" % link) view(link) print("\npress ctrl-C to exit")
def setup_plot(self): """" code to setup the bokeh plots """ # make bokeh figure tools = [ "tap", "box_zoom", "lasso_select", "box_select", "wheel_zoom", "pan", "reset", ] self.p = figure( x_range=(self.peakipy_data.f2_ppm_0, self.peakipy_data.f2_ppm_1), y_range=(self.peakipy_data.f1_ppm_0, self.peakipy_data.f1_ppm_1), x_axis_label=f"{self.peakipy_data.f2_label} - ppm", y_axis_label=f"{self.peakipy_data.f1_label} - ppm", tools=tools, active_drag="pan", active_scroll="wheel_zoom", active_tap=None, ) if not self.thres: self.thres = threshold_otsu(self.peakipy_data.data[0]) self.contour_start = self.thres # contour level start value self.contour_num = 20 # number of contour levels self.contour_factor = 1.20 # scaling factor between contour levels cl = self.contour_start * self.contour_factor ** np.arange(self.contour_num) if len(cl) > 1 and np.min(np.diff(cl)) <= 0.0: print(f"Setting contour levels to np.abs({cl})") cl = np.abs(cl) self.extent = ( self.peakipy_data.f2_ppm_0, self.peakipy_data.f2_ppm_1, self.peakipy_data.f1_ppm_0, self.peakipy_data.f1_ppm_1, ) self.spec_source = get_contour_data( self.peakipy_data.data[0], cl, extent=self.extent, cmap=viridis ) # negative contours self.spec_source_neg = get_contour_data( self.peakipy_data.data[0] * -1.0, cl, extent=self.extent, cmap=autumn ) self.p.multi_line( xs="xs", ys="ys", line_color="line_color", source=self.spec_source ) self.p.multi_line( xs="xs", ys="ys", line_color="line_color", source=self.spec_source_neg ) # contour_num = Slider(title="contour number", value=20, start=1, end=50,step=1) # contour_start = Slider(title="contour start", value=100000, start=1000, end=10000000,step=1000) self.contour_start = TextInput( value="%.2e" % self.thres, title="Contour level:", width=100 ) # contour_factor = Slider(title="contour factor", value=1.20, start=1., end=2.,step=0.05) self.contour_start.on_change("value", self.update_contour) # for w in [contour_num,contour_start,contour_factor]: # w.on_change("value",update_contour) # plot mask outlines el = self.p.ellipse( x="X_PPM", y="Y_PPM", width="X_DIAMETER_PPM", height="Y_DIAMETER_PPM", source=self.source, fill_color="color", fill_alpha=0.1, line_dash="dotted", line_color="red", ) self.p.add_tools( HoverTool( tooltips=[ ("Index", "$index"), ("Assignment", "@ASS"), ("CLUSTID", "@CLUSTID"), ("RADII", "@X_RADIUS_PPM{0.000}, @Y_RADIUS_PPM{0.000}"), ( f"{self.peakipy_data.f2_label},{self.peakipy_data.f1_label}", "$x{0.000} ppm, $y{0.000} ppm", ), ], mode="mouse", # add renderers renderers=[el], ) ) # p.toolbar.active_scroll = "auto" # draw border around spectrum area spec_border_x = [ self.peakipy_data.f2_ppm_min, self.peakipy_data.f2_ppm_min, self.peakipy_data.f2_ppm_max, self.peakipy_data.f2_ppm_max, self.peakipy_data.f2_ppm_min, ] spec_border_y = [ self.peakipy_data.f1_ppm_min, self.peakipy_data.f1_ppm_max, self.peakipy_data.f1_ppm_max, self.peakipy_data.f1_ppm_min, self.peakipy_data.f1_ppm_min, ] self.p.line( spec_border_x, spec_border_y, line_width=1, line_color="black", line_dash="dotted", line_alpha=0.5, ) self.p.circle(x="X_PPM", y="Y_PPM", source=self.source, color="color") # plot cluster numbers self.p.text( x="X_PPM", y="Y_PPM", text="CLUSTID", text_color="color", source=self.source, text_font_size="8pt", text_font_style="bold", ) self.p.on_event(DoubleTap, self.peak_pick_callback) self.pos_neg_contour_dic = {0: "pos/neg", 1: "pos", 2: "neg"} self.pos_neg_contour_radiobutton = RadioButtonGroup( labels=[ self.pos_neg_contour_dic[i] for i in self.pos_neg_contour_dic.keys() ], active=0, ) self.pos_neg_contour_radiobutton.on_change("active", self.update_contour) # call fit_peaks self.fit_button = Button(label="Fit selected cluster", button_type="primary") # lineshape selection self.lineshapes = { 0: "PV", 1: "V", 2: "G", 3: "L", 4: "PV_PV", # 5: "PV_L", # 6: "PV_G", # 7: "G_L", } self.radio_button_group = RadioButtonGroup( labels=[self.lineshapes[i] for i in self.lineshapes.keys()], active=0 ) self.ls_div = Div( text="""Choose lineshape you wish to fit. This can be Voigt (V), pseudo-Voigt (PV), Gaussian (G), Lorentzian (L). PV_PV fits a PV lineshape with independent "fraction" parameters for the direct and indirect dimensions""" ) self.clust_div = Div( text="""If you want to adjust how the peaks are automatically clustered then try changing the width/diameter/height (integer values) of the structuring element used during the binary dilation step (you can also remove it by selecting 'None'). Increasing the size of the structuring element will cause peaks to be more readily incorporated into clusters. Be sure to save your peak list before doing this as any manual edits will be lost.""" ) self.intro_div = Div( text="""<h2>peakipy - interactive fit adjustment </h2> """ ) self.doc_link = Div( text="<h3><a href='https://j-brady.github.io/peakipy/build/usage/instructions.html', target='_blank'> ℹ️ click here for documentation</a></h3>" ) self.fit_reports = "" self.fit_reports_div = Div(text="", height=400, style={"overflow": "scroll"}) # Plane selection self.select_planes_list = [ f"{i}" for i in range(self.peakipy_data.data.shape[self.peakipy_data.planes]) ] self.select_plane = Select( title="Select plane:", value=self.select_planes_list[0], options=self.select_planes_list, ) self.select_planes_dic = { f"{i}": i for i in range(self.peakipy_data.data.shape[self.peakipy_data.planes]) } self.select_plane.on_change("value", self.update_contour) self.checkbox_group = CheckboxGroup( labels=["fit current plane only"], active=[] ) # not sure this is needed selected_df = self.peakipy_data.df.copy() self.fit_button.on_event(ButtonClick, self.fit_selected) columns = [ TableColumn(field="ASS", title="Assignment"), TableColumn(field="CLUSTID", title="Cluster", editor=IntEditor()), TableColumn( field="X_PPM", title=f"{self.peakipy_data.f2_label}", editor=NumberEditor(step=0.0001), formatter=NumberFormatter(format="0.0000"), ), TableColumn( field="Y_PPM", title=f"{self.peakipy_data.f1_label}", editor=NumberEditor(step=0.0001), formatter=NumberFormatter(format="0.0000"), ), TableColumn( field="X_RADIUS_PPM", title=f"{self.peakipy_data.f2_label} radius (ppm)", editor=NumberEditor(step=0.0001), formatter=NumberFormatter(format="0.0000"), ), TableColumn( field="Y_RADIUS_PPM", title=f"{self.peakipy_data.f1_label} radius (ppm)", editor=NumberEditor(step=0.0001), formatter=NumberFormatter(format="0.0000"), ), TableColumn( field="XW_HZ", title=f"{self.peakipy_data.f2_label} LW (Hz)", editor=NumberEditor(step=0.01), formatter=NumberFormatter(format="0.00"), ), TableColumn( field="YW_HZ", title=f"{self.peakipy_data.f1_label} LW (Hz)", editor=NumberEditor(step=0.01), formatter=NumberFormatter(format="0.00"), ), TableColumn( field="VOL", title="Volume", formatter=NumberFormatter(format="0.0") ), TableColumn( field="include", title="Include", editor=SelectEditor(options=["yes", "no"]), ), TableColumn(field="MEMCNT", title="MEMCNT", editor=IntEditor()), ] self.data_table = DataTable( source=self.source, columns=columns, editable=True, fit_columns=True ) # callback for adding # source.selected.on_change('indices', callback) self.source.selected.on_change("indices", self.select_callback) # Document layout fitting_controls = column( row( column(self.slider_X_RADIUS, self.slider_Y_RADIUS), column( row( widgetbox(self.contour_start, self.pos_neg_contour_radiobutton) ), widgetbox(self.fit_button), ), ), row( column(widgetbox(self.ls_div), widgetbox(self.radio_button_group)), column(widgetbox(self.select_plane), widgetbox(self.checkbox_group)), ), ) # reclustering tab self.struct_el = Select( title="Structuring element:", value="disk", options=["square", "disk", "rectangle", "None", "mask_method"], width=100, ) self.struct_el_size = TextInput( value="3", title="Size(width/radius or width,height for rectangle):", width=100, ) self.recluster = Button(label="Re-cluster", button_type="warning") self.recluster.on_event(ButtonClick, self.recluster_peaks) # edit_fits tabs fitting_layout = fitting_controls log_layout = self.fit_reports_div recluster_layout = row( self.clust_div, column( self.contour_start, self.struct_el, self.struct_el_size, self.recluster ), ) save_layout = column(self.savefilename, self.button, self.exit_button) fitting_tab = Panel(child=fitting_layout, title="Peak fitting") log_tab = Panel(child=log_layout, title="Log") recluster_tab = Panel(child=recluster_layout, title="Re-cluster peaks") save_tab = Panel(child=save_layout, title="Save edited peaklist") self.tabs = Tabs( tabs=[fitting_tab, log_tab, recluster_tab, save_tab], sizing_mode="scale_both", )
zoom.on_change('value', update1) lat = Slider(start=36, end=36.24, value=36.1200, step=.02, title="Lat Position") lat.on_change('value', update1) lon = Slider(start=-115.30, end=-115.06, value=-115.18, step=.02, title="Lon Position") lon.on_change('value', update1) # Time Control days_options = RadioButtonGroup(labels=["M", "T", "W", "T", "F", "S", "S"], active=5) days_options.on_change('active', update1) slider = Slider(start=0, end=23, value=0, step=1, title="Hour of Day") slider.on_change('value', update1) # Select Options neighborhood_options = list(set(df['neighborhood']))[1:] alcohol_options = ['beer_and_wine', 'full_bar', 'none'] ambience = df['Ambience'][0] ambience_options = [*ambience] category_options = ['Casinos', 'Buffets', 'Nightlife', 'Cafes'] # Select Widgets neighborhood = Select(title='Neighborhood', value='All',
# Corresponds to unscaled if scaling.active == 0: region_selection1.active = worst_d["worst9inds1"] region_selection2.active = worst_d["worst9inds2"] # Corresponds to per capita else: region_selection1.active = worst_d["worst9inds1_capita"] region_selection2.active = worst_d["worst9inds2_capita"] worst9.button_type = "primary" #-----------------------------------------------------------------------------# # Radio button group for selecting cases vs deaths data_type = RadioButtonGroup(labels=["Cases", "Deaths"], active=0, css_classes=["custom_button"]) data_type.on_change("active", update_plot) # Radio button group for selecting unscaled vs per capita scaling = RadioButtonGroup(labels=["Unscaled", "Per 100,000"], active=0, css_classes=["custom_button"]) scaling.on_change("active", update_plot) # Select and unselect all select_all = Button(label="Select All", css_classes=["custom_button"]) select_all.on_click(select_all_update) unselect_all = Button(label="Unselect All", css_classes=["custom_button"]) unselect_all.on_click(unselect_all_update) # Button for displaying worst 9 regions worst9 = Button(label="Show Worst 9 States", css_classes=["custom_button"])#, button_type="success")
depreciation.active.toString(), deductibility.active.toString()]; var reform_sources = %s; ref_source.data = reform_sources[parts.join('_')].data; // set baseline data source var bs_source = individual.active === 1 ? base_metr : base_mettr; base_source.data = bs_source.data; """ % js_source_array callback = CustomJS(args=reform_sources, code=reform_source_change_code) # Create buttons rate_buttons = RadioButtonGroup( labels=["39.6%", "35%", "30%", "25%", "20%", "15%", "0%"], active=1, callback=callback, width=500) depreciation_buttons = RadioButtonGroup(labels=[ "Economic", "Current Law, No Bonus", "Current Law", "Full Expensing" ], active=2, callback=callback, width=500) deductibility_buttons = RadioButtonGroup( labels=["Fully Deductible", "Non-Deductible"], active=0, callback=callback, width=500) individual_buttons = RadioButtonGroup(labels=["Yes", "No"], active=0,
def __init__(self, model): """ Construct storage dashboard """ # Save reference to model self.model = model ################################ # Process button ################################ self.process = Button(label="Generate", button_type="primary", name='process', sizing_mode='scale_width') self.process.js_on_click(CustomJS(code="toggleLoading()")) ################################ # Widgets ################################ # Data type selection self.data_type = RadioButtonGroup( labels=["All Data", "Experimental", "Simulated"], active=0, css_classes=['dtypes']) # Adsorbate drop-down selections self.g1_sel = Select(title="Adsorbate 1", options=self.model.ads_list, value=self.model.g1, css_classes=['g-selectors']) # Temperature selection self.t_absolute = Spinner(value=303, title='Temperature:', css_classes=['t_abs']) self.t_tolerance = Spinner(value=10, title='Tolerance:', css_classes=['t_tol']) # Combined in a layout self.dsel_widgets = layout([ [self.data_type], [self.g1_sel, self.g2_sel, self.t_absolute, self.t_tolerance], ], sizing_mode='scale_width', name="widgets") ################################ # KPI Plots ################################ # Top graph generation tooltip = load_tooltip() self.p_henry, rend1 = self.top_graph("K", "Henry coefficient (log)", self.model.data, self.model.errors, tooltip) self.p_loading, rend2 = self.top_graph("L", "Uptake at selected pressure", self.model.data, self.model.errors, tooltip) self.p_wc, rend3 = self.top_graph( "W", "Working capacity in selected range", self.model.data, self.model.errors, tooltip) # Give graphs the same hover and select effect sel = Circle(fill_alpha=1, fill_color="red", line_color='black') for rend in [rend1, rend2, rend3]: rend.selection_glyph = sel rend.hover_glyph = sel # Pressure slider self.p_slider = Slider( title="Pressure (bar)", value=0.5, start=0, end=20, step=0.5, callback_policy='throttle', callback_throttle=500, ) # Working capacity slider self.wc_slider = RangeSlider( title="Working capacity (bar)", value=(0.5, 5), start=0, end=20, step=0.5, callback_policy='throttle', callback_throttle=500, ) # Material datatable self.mat_list = DataTable( columns=[ TableColumn(field="labels", title="Material", width=300), TableColumn(field="sel", title="KH2/KH1", width=25, formatter=NumberFormatter(format='‘0.0a’')), TableColumn(field="psa_W", title="PSA-API", width=30, formatter=NumberFormatter(format='‘0.0a’')), ], source=self.model.data, index_position=None, selectable='checkbox', scroll_to_selection=True, width=400, fit_columns=True, ) # Custom css classes for interactors self.p_henry.css_classes = ['g-henry'] self.p_loading.css_classes = ['g-load'] self.p_wc.css_classes = ['g-wcap'] self.mat_list.css_classes = ['t-details'] # Generate the axis labels self.top_graph_labels() self.kpi_plots = layout([ [ gridplot([[self.mat_list, self.p_henry], [self.p_loading, self.p_wc]], sizing_mode='scale_width') ], [self.p_slider, self.wc_slider], ], sizing_mode='scale_width', name="kpiplots") self.kpi_plots.children[0].css_classes = ['kpi'] self.kpi_plots.children[1].css_classes = ['p-selectors'] ################################ # Isotherm details explorer ################################ # Isotherm display graphs self.p_g1iso = self.bottom_graph(self.model.g1_iso_sel, self.model.g1) self.p_g2iso = self.bottom_graph(self.model.g2_iso_sel, self.model.g2) # Isotherm display palette self.c_cyc = cycle(gen_palette(20)) self.detail_plots = layout([ [self.p_g1iso, self.p_g2iso], ], sizing_mode='scale_width', name="detailplots") self.detail_plots.children[0].css_classes = ['isotherms']