result = most_common source.trigger('change') #dropdown zipcode input zip_menu = [("94102", "94102"), ("94103", "94103"), ("94104", "94104"), ("94105", "94105"), ("94107", "94107"), ("94108", "94108"), ("94109", "94109"), ("94110", "94110"), ("94111", "94111"), ("94112", "94112"), ("94114", "94114"), ("94115", "94115"), ("94116", "94116"), ("94117", "94117"), ("94118", "94118"), ("94121", "94121"), ("94122", "94122"), ("94123", "94123"), ("94124", "94124"), ("94127", "94127"), ("94129", "94129"), ("94130", "94130"), ("94131", "94131"), ("94132", "94132"), ("94133", "94133"), ("94134", "94134"), ("94158", "94158")] zipdrop = Dropdown(label="Zipcode", button_type="warning", menu=zip_menu, callback=CustomJS.from_py_func(callback)) z = zipdrop.value #dropdown time input menu = [("12:00 AM", "0"), ("1:00 AM", "1"), ("2:00 AM", "2"), ("3:00 AM", "3"), ("4:00 AM", "4"), ("5:00 AM", "5"), ("6:00 AM", "6"), ("7:00 AM", "7"), ("8:00 AM", "8"), ("9:00 AM", "9"), ("10:00 AM", "10"), ("11:00 AM", "11"), ("12:00 PM", "12"), ("1:00 PM", "13"), ("2:00 PM", "14"), ("3:00 PM", "15"), ("4:00 PM", "16"), ("5:00 PM", "17"), ("6:00 PM", "18"), ("7:00 PM", "19"), ("8:00 PM", "20"), ("9:00 PM", "21"), ("10:00 PM", "22"), ("11:00 PM", "23")] timedrop = Dropdown(label="Time of Day", button_type="warning", menu=menu,
#StringFormatter, NumberFormatter, #StringEditor, IntEditor, NumberEditor, SelectEditor, ) menu = [("Item 1", "1"), ("Item 2", "2"), ("Item 3", "3")] layout = column( Button(label="Default Button 1", button_type="default"), Button(label="Primary Button 2", button_type="primary"), Button(label="Success Button 3", button_type="success"), Toggle(label="Default Toggle 1", button_type="default"), Toggle(label="Primary Toggle 2", button_type="primary"), Toggle(label="Success Toggle 3", button_type="success"), Dropdown(label="Default Dropdown 1", button_type="default", menu=menu), 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"),
def make_dropdown(prop, menu): dropdown = Dropdown(label=prop, menu=menu) add_callback(dropdown, prop) return dropdown
alpha=0.6, line_color="black", line_width=2) ds3 = p3.data_source p4 = plot4.circle(x='xdata4', y='ydata4', source=source, size='size4', color='ColorList4', alpha=0.6, line_color="black", line_width=2) ds4 = p4.data_source dropdownX1 = Dropdown(label="X value1", button_type="default", menu=menu) dropdownX2 = Dropdown(label="X value2", button_type="default", menu=menu) dropdownX3 = Dropdown(label="X value3", button_type="default", menu=menu) dropdownX4 = Dropdown(label="X value4", button_type="default", menu=menu) def dropdownX_handler1(new): dictionary['xdata1'] = dictionary[new.item] ds1.data = dictionary plot1.xaxis.axis_label = new.item def dropdownX_handler2(new): dictionary['xdata2'] = dictionary[new.item] ds2.data = dictionary plot2.xaxis.axis_label = new.item
x = [3, 4, 6, 12, 10, 1] y = [7, 1, 3, 4, 1, 6] c = ['blue', 'blue', 'blue', 'blue', 'blue', 'blue'] source = ColumnDataSource(data=dict(x=x, y=y, color=c)) plot_figure = figure(title='Dropdown', plot_height=450, plot_width=600, tools="save,reset", toolbar_location="below") plot_figure.scatter('x', 'y', color='color', source=source, size=10) menu = [("Red", "red"), ("Green", "green")] dropdown = Dropdown(label="Choose color for plot", menu=menu) def dropdown_click(attr, old, new): active_dropdown = dropdown.value ##Getting dropdown value if active_dropdown == 'red': c = ['red', 'red', 'red', 'red', 'red', 'red'] elif active_dropdown == 'green': c = ['green', 'green', 'green', 'green', 'green', 'green'] source.data = dict(x=x, y=y, color=c) dropdown.on_change('value', dropdown_click) layout = row(dropdown, plot_figure)
from bokeh.sampledata.iris import flowers from bokeh.util.browser import view click_button = Button(label="Button still has click event", button_type="success") disabled_button = Button(label="Button (disabled) - still has click event", button_type="primary", disabled=True) toggle = Toggle(label="Toggle button", button_type="success") menu = [("Item 1", "item_1_value"), ("Item 2", "item_2_value"), None, ("Item 3", "item_3_value")] dropdown = Dropdown(label="Dropdown button", button_type="warning", menu=menu) dropdown_split = Dropdown(label="Split button", button_type="danger", menu=menu, split=True) checkbox_group = CheckboxGroup(labels=["Option 1", "Option 2", "Option 3"], active=[0, 1]) radio_group = RadioGroup(labels=["Option 1", "Option 2", "Option 3"], active=0) checkbox_button_group = CheckboxButtonGroup( labels=["Option 1", "Option 2", "Option 3"], active=[0, 1]) radio_button_group = RadioButtonGroup( labels=["Option 1", "Option 2", "Option 3"], active=0) checkbox_button_group_vertical = CheckboxButtonGroup(
title='Select Y Axis', options=sorted(head_columns.keys()), value='Amplifier words' ) sel_xaxis = Select( title='Select X Axis', options=sorted(head_columns.keys()), value='Outcome' ) options=['scatter', 'vbar', 'varea', 'line'] sel_plot = Dropdown( label='Select plot type', button_type='success', menu=options, value='line' ) add_row = FileInput( ) inp_id = TextInput( placeholder='Insert ID', value='1', ) sh_ess = TextAreaInput( title='Essay %s: 338 words' % inp_id.value, rows=30, cols=50,
start=0, end=3, step=0.02) r0_slider = Slider(title="Basic reproduction number", value=r0, start=0, end=3, step=0.02) Nt_slider = Slider(title="Total time (days)", value=Nt, start=10, end=max_Nt, step=1) country_opts = country_list dropdown = Dropdown(labe="Select Country", button_type="warning", menu=country_opts) def update_data(attrname, old, new): # Get the current slider values a = N0_slider.value b = r_slider.value c = dt d = Nt_slider.value e = r0_slider.value f = dropdown.value D = np.zeros(d + 2) D[0] = a
def categoricalPlot(dataset_id, columnName): log = DatasetManager.query.get_or_404(dataset_id) datasetName = log.datasetName datasetSqlName = log.datasetSqlName df = pd.read_sql_table(datasetSqlName, db.engine) vc = df[columnName].value_counts() categories = vc.index.to_list() # Categorical values can also be used as coordinates values = vc.to_list() print("categories and values:", categories, values) source = ColumnDataSource(data=dict(categories=categories, values=values)) print("source dir=", dir(source)) print("source.data=", source.data) print("source.data=", source.data["categories"]) # Set the x_range to the list of categories above p = figure( x_range=source.data["categories"], plot_height=250, plot_width=1100, title=datasetName + ": " + columnName + " Counts", ) p.vbar( x="categories", top="values", width=0.9, source=source, legend_field="categories", line_color="white", fill_color=factor_cmap("categories", palette=Spectral6, factors=source.data["categories"]), ) # Set some properties to make the plot look better p.xgrid.grid_line_color = None p.y_range.start = 0 # p.y_range.end = 9 p.legend.orientation = "horizontal" p.legend.location = "top_center" # Create menu and options with name of data columns with numerical data columns = df.columns.to_list() menu = [] columnNameList = [] category_counts = [] for columnName in columns: if not (is_numeric_dtype(df[columnName])): # Menu is a list of tuples of name/value pairs for dropdown widget menu.append((columnName, columnName)) # Option is a list of column names for multi-select widget columnNameList.append(columnName) # Get value counts vc = df[columnName].value_counts() category_counts.append([vc.index.to_list(), vc.to_list()]) # print("category_counts =", category_counts) # Create dropdown widget dropdown = Dropdown(label="Select Column", button_type="primary", menu=menu, width_policy="min") category_counts_source = ColumnDataSource( data=dict(x=columnNameList, y=category_counts)) # Define JS callback to change column to plot print("p.x_range=", p.x_range.properties_with_values()) print(dir(p.x_range)) callback_dropdown = CustomJS( args=dict(source=source, category_counts=category_counts_source, x_range=p.x_range), code=""" console.log('dropdown=' + this.item); // Initialize x and y arrays var data = source.data; var categories = data['categories']; // console.log(' x=' + x); var values = data['values']; // console.log('y=' + y); console.log('categories=' + categories + ' values=' + values); // Get dataframe var category_counts_data = category_counts.data; console.log('category_counts_data=' + category_counts_data); console.dir(category_counts_data); console.log('category_counts_data.x=' + category_counts_data.x); console.dir(category_counts_data.x); console.dir(category_counts_data.y); var index = category_counts_data.x.indexOf(this.item); console.log('index=' + index); console.log(category_counts_data.y[index]); // categories = category_counts_data.y[index][0]; // values = category_counts_data.y[index][1]; source.data['categories'] = category_counts_data.y[index][0]; source.data['values'] = category_counts_data.y[index][1]; x_range.factors = source.data['categories']; console.log('categories=' + categories + ' values=' + values); // Update plot with new x,y source data source.change.emit(); """, ) # Define javascript events to trigger callbacks dropdown.js_on_event( "menu_item_click", callback_dropdown, ) # return row(dropdown, p) return p
def widgets(): from bokeh.io import show from bokeh.models import Select, CheckboxButtonGroup, Button, CheckboxGroup, ColorPicker, Dropdown, \ FileInput, MultiSelect, RadioButtonGroup, RadioGroup, Slider, RangeSlider, TextAreaInput, TextInput, Toggle, \ Paragraph, PreText, Div put_text('Button') button = Button(label="Foo", button_type="success") show(button) put_text('CheckboxButtonGroup') checkbox_button_group = CheckboxButtonGroup( labels=["Option 1", "Option 2", "Option 3"], active=[0, 1]) show(checkbox_button_group) put_text('CheckboxGroup') checkbox_group = CheckboxGroup(labels=["Option 1", "Option 2", "Option 3"], active=[0, 1]) show(checkbox_group) put_text('ColorPicker') color_picker = ColorPicker(color="#ff4466", title="Choose color:", width=200) show(color_picker) put_text('Dropdown') menu = [("Item 1", "item_1"), ("Item 2", "item_2"), None, ("Item 3", "item_3")] dropdown = Dropdown(label="Dropdown button", button_type="warning", menu=menu) show(dropdown) put_text('FileInput') file_input = FileInput() show(file_input) put_text('MultiSelect') multi_select = MultiSelect(title="Option:", value=["foo", "quux"], options=[("foo", "Foo"), ("bar", "BAR"), ("baz", "bAz"), ("quux", "quux")]) show(multi_select) put_text('RadioButtonGroup') radio_button_group = RadioButtonGroup( labels=["Option 1", "Option 2", "Option 3"], active=0) show(radio_button_group) put_text('RadioGroup') radio_group = RadioGroup(labels=["Option 1", "Option 2", "Option 3"], active=0) show(radio_group) put_text('Select') select = Select(title="Option:", value="foo", options=["foo", "bar", "baz", "quux"]) show(select) put_text('Slider') slider = Slider(start=0, end=10, value=1, step=.1, title="Stuff") show(slider) put_text('RangeSlider') range_slider = RangeSlider(start=0, end=10, value=(1, 9), step=.1, title="Stuff") show(range_slider) put_text('TextAreaInput') text_input = TextAreaInput(value="default", rows=6, title="Label:") show(text_input) put_text('TextInput') text_input = TextInput(value="default", title="Label:") show(text_input) put_text('Toggle') toggle = Toggle(label="Foo", button_type="success") show(toggle) put_text('Div') div = Div( text= """Your <a href="https://en.wikipedia.org/wiki/HTML">HTML</a>-supported text is initialized with the <b>text</b> argument. The remaining div arguments are <b>width</b> and <b>height</b>. For this example, those values are <i>200</i> and <i>100</i> respectively.""", width=200, height=100) show(div) put_text('Paragraph') p = Paragraph( text="""Your text is initialized with the 'text' argument. The remaining Paragraph arguments are 'width' and 'height'. For this example, those values are 200 and 100 respectively.""", width=200, height=100) show(p) put_text('PreText') pre = PreText(text="""Your text is initialized with the 'text' argument. The remaining Paragraph arguments are 'width' and 'height'. For this example, those values are 500 and 100 respectively.""", width=500, height=100) show(pre)
bar_chart2.visible = False bar_chart3.visible = False elif new == '2e Klass': bar_chart.visible = False bar_chart1.visible = False bar_chart2.visible = True bar_chart3.visible = False elif new == '3e Klass': bar_chart.visible = False bar_chart1.visible = False bar_chart2.visible = False bar_chart3.visible = True # Create a dropdown Select widget: select selectRegio = Dropdown(label="Maak keuze uit de klasse", menu=["All", "1e Klass", "2e Klass", "3e Klass"]) # Attach the update_plot callback to the 'value' property of select selectRegio.on_click(update_bar_chart) # Hoofdstuk 3 Scatter en 2-D visualisatie titel4 = Div(text="<h2" ">Hoofdstuk 3: Scatterplots en 2-D visualisaties" "</h2>", width=800, height=50) text4 = Div( text="<h4" ">Hieronder volgt scatterplot van prijs per ticket tegenover de GDP per capita" "</h4>", width=800,
def test_server_on_change_round_trip( self, bokeh_server_page: BokehServerPage) -> None: button = Dropdown(label="Dropdown button", menu=items) def modify_doc(doc): source = ColumnDataSource(dict(x=[1, 2], y=[1, 1])) plot = Plot(height=400, width=400, x_range=Range1d(0, 1), y_range=Range1d(0, 1), min_border=0) plot.add_glyph(source, Circle(x='x', y='y', size=20)) plot.tags.append( CustomJS(name="custom-action", args=dict(s=source), code=RECORD("data", "s.data"))) def cb(event): item = event.item if item == "item_1_value": source.data = dict(x=[10, 20], y=[10, 10]) elif item == "item_2_value": source.data = dict(x=[100, 200], y=[100, 100]) elif item == "item_3_value": source.data = dict(x=[1000, 2000], y=[1000, 1000]) button.on_event('menu_item_click', cb) doc.add_root(column(button, plot)) page = bokeh_server_page(modify_doc) button_el = find_element_for(page.driver, button, "button") button_el.click() item = find_element_for(page.driver, button, ".bk-menu > *:nth-child(1)") item.click() page.eval_custom_action() results = page.results assert results == {'data': {'x': [10, 20], 'y': [10, 10]}} button_el = find_element_for(page.driver, button, "button") button_el.click() item = find_element_for(page.driver, button, ".bk-menu > *:nth-child(3)") item.click() page.eval_custom_action() results = page.results assert results == {'data': {'x': [1000, 2000], 'y': [1000, 1000]}} button_el = find_element_for(page.driver, button, "button") button_el.click() item = find_element_for(page.driver, button, ".bk-menu > *:nth-child(2)") item.click() page.eval_custom_action() results = page.results assert results == {'data': {'x': [100, 200], 'y': [100, 100]}}
fill_color=factor_cmap('x', palette=palette, factors=result['genders'], start=1, end=2)) # create a plot and style its properties p.y_range.start = 0 p.x_range.range_padding = 0.025 p.xaxis.major_label_orientation = 1 p.xgrid.grid_line_color = None # create a callback def dropdown_callback(attr, old, new): result = getMedals(new) counts = sum(zip(result['data']['men'], result['data']['women']), ()) p.x_range = FactorRange(*result['range']) r.data_source.data = dict(x=result['range'], counts=counts) # button details, range restrictions for display, with the callback menu = [("1900", "1900"), ("1920", "1920"), ("1950", "1950"), ("1960", "1960"), ("1970", "1970"), ("1980", "1980"), ("1990", "1990"), ("2000", "2000"), ("2010", "2010"), ("2016", "2016")] dropdown = Dropdown(label="Start Year", button_type="warning", menu=menu) dropdown.on_change("value", dropdown_callback) # add button to the graph page curdoc().add_root(column(dropdown, p))
}, 'eta_modifier': { 'SGD': 50, 'AdaGrad': 50, 'RMSProp': 1, 'Momemtum': 50, 'Nesterov': 50, 'Adam': 5, 'Nadam': 5, 'Activate': 50 } } dropdowns = { 'activation': Dropdown(label='Sigmoid', menu=menus['activation'], width=245), 'optimizer': Dropdown(label='SGD', menu=menus['optimizer'], width=245) } for name, dropdown in dropdowns.items(): dropdown.on_click( partial(callback_dropdown, ref=dropdown, selector=selectors[name])) configs = {'num_tiles': 500} weight_0, weight_1 = np.meshgrid( np.linspace(-20., 20., num=configs['num_tiles']), np.linspace(-20., 20., num=configs['num_tiles'])) tf.random.set_seed(0)
def _setup_widgets(self): """ Create annotator widgets and assign Python callbacks. """ from bokeh.models import TextInput, Button, Dropdown super()._setup_widgets() self.annotator_input = TextInput(title="Label:") self.annotator_apply = Button( label="Apply", button_type="primary", height_policy="fit", width_policy="min", ) self.annotator_export = Dropdown( label="Export", button_type="warning", menu=["Excel", "CSV", "JSON", "pickle"], height_policy="fit", width_policy="min", ) def callback_apply(): """ A callback on clicking the 'self.annotator_apply' button. Update labels in the source. """ label = self.annotator_input.value selected_idx = self.sources["raw"].selected.indices if not selected_idx: self._warn( "Attempting annotation: did not select any data points. Eligible subset is 'raw'." ) return example_old = self.dfs["raw"].at[selected_idx[0], "label"] self.dfs["raw"].at[selected_idx, "label"] = label example_new = self.dfs["raw"].at[selected_idx[0], "label"] self._good( f"Applied {len(selected_idx)} annotations: {label} (e.g. {example_old} -> {example_new}" ) self._update_sources() self.plot() self._good(f"Updated annotator plot at {current_time()}") def callback_export(event, path_root=None): """ A callback on clicking the 'self.annotator_export' button. Saves the dataframe to a pickle. """ export_format = event.item # auto-determine the export path root if path_root is None: timestamp = current_time("%Y%m%d%H%M%S") path_root = f"hover-annotated-df-{timestamp}" export_df = pd.concat(self.dfs, axis=0, sort=False, ignore_index=True) if export_format == "Excel": export_path = f"{path_root}.xlsx" export_df.to_excel(export_path, index=False) elif export_format == "CSV": export_path = f"{path_root}.csv" export_df.to_csv(export_path, index=False) elif export_format == "JSON": export_path = f"{path_root}.json" export_df.to_json(export_path, orient="records") elif export_format == "pickle": export_path = f"{path_root}.pkl" export_df.to_pickle(export_path) else: raise ValueError(f"Unexpected export format {export_format}") self._good(f"Saved DataFrame to {export_path}") # keep the references to the callbacks self._callback_apply = callback_apply self._callback_export = callback_export # assign callbacks self.annotator_apply.on_click(self._callback_apply) self.annotator_export.on_click(self._callback_export)
def multilinePlot(dataset_id, columnName): log = DatasetManager.query.get_or_404(dataset_id) datasetName = log.datasetName datasetSqlName = log.datasetSqlName df = pd.read_sql_table(datasetSqlName, db.engine) # prepare some data x = df["index"] # create a new plot with a title and axis labels p = figure( title="Dataset Name: " + datasetName, x_axis_label="Index", y_axis_label="y", sizing_mode="fixed", plot_width=1100, plot_height=400, ) # Initialize source to x=[x] and y=[x] # Note: # Multi-line plots take an array as x's and y's # The x and y arrays must be the same length initialColumnName = columnName y = df[columnName] source = ColumnDataSource(data=dict(x=[x], y=[y])) df_source = ColumnDataSource(data=dict(df)) p.multi_line("x", "y", source=source) # Create menu and options with name of data columns with numerical data columns = df.columns.to_list() menu = [] options = [] for columnName in columns: # if df[columnName].dtype == np.float64 or df[columnName].dtype == np.int64: if is_numeric_dtype(df[columnName]): # Menu is a list of tuples of name/value pairs for dropdown widget menu.append((columnName, columnName)) # Option is a list of column names for multi-select widget options.append(columnName) # Create dropdown widget dropdown = Dropdown(label="Select Column", button_type="primary", menu=menu, width_policy="min") # Create multi-select widget multi_choice = MultiChoice(value=[initialColumnName], options=options, title="Select Columns") # Define JS callback to change column to plot callback_dropdown = CustomJS( args=dict(source=source, df_source=df_source), code=""" // Initialize x and y arrays var data = source.data; var x = data['x']; // console.log(' x=' + x); var y = data['y']; // console.log('y=' + y); // Get dataframe var df_data = df_source.data; // Set y array to selected value y[0] = df_data[this.item]; // Update plot with new x,y source data source.change.emit(); """, ) callback_multi_select = CustomJS( args=dict(source=source, df_source=df_source), code=""" // Initialize x and y arrays var data = source.data; var x = data['x']; var y = data['y']; x.length = 0; y.length = 0; // console.log(' x=' + x); // console.log('x.length=' + x.length); // console.log('y=' + y); // console.log('y.length=' + y.length); // Get dataframe var df_data = df_source.data; // Get value of multi-select widget // console.log('multi_select: ' + this.value, this.value.toString()); var array = this.value; // Iterate through multi-select values and update x,y arrays with selected values var array_iterator = array.values(); let next_value = array_iterator.next(); while (!next_value.done) { // console.log(next_value.value); x.push(df_data['index']); y.push(df_data[next_value.value]); next_value = array_iterator.next(); } // console.log('y=' + y); // console.log('y.length=' + y.length); // console.log('x.length=' + x.length); // Update plot with new x,y source data source.change.emit(); """, ) # Define javascript events to trigger callbacks dropdown.js_on_event( "menu_item_click", callback_dropdown, ) multi_choice.js_on_change( "value", callback_multi_select, ) layout = column(multi_choice, p) return layout
from bokeh.io import show from bokeh.models import CustomJS, Dropdown menu = [("Item 1", "item_1"), ("Item 2", "item_2"), None, ("Item 3", "item_3")] dropdown = Dropdown(label="Dropdown button", button_type="warning", menu=menu) dropdown.js_on_event( "menu_item_click", CustomJS(code="console.log('dropdown: ' + this.item, this.toString())")) show(dropdown)
) alarm.visible = False alarm_list = Div( text= '<div class="container"><h3>Dispositivos que presentan problemas:</h3><ul></ul></div>' ) alarm_list.visible = False dataRecordingButton = Button(label="Comenzar a adquirir datos", button_type="success") dataRecordingLabel = Div(text='<p>Adquiriendo datos...</p>') dataRecordingLabel.visible = False extensionsMenu = [("csv", "csv"), ("txt", "txt"), ("npy", "npy")] extensionsDropdown = Dropdown(label="Guardar los datos con extensión:", button_type="success", menu=extensionsMenu) ''' ******************** Modo Automático ******************** ''' label1 = Div(text='<h1>Modo Automático ⚡</h1><hr>') refEst1 = Slider(title="Altura de Referencia Estanque 1", value=ref1, start=0.0, end=50.0, step=0.1) refEst2 = Slider(title="Altura de Referencia Estanque 2", value=ref2, start=0.0, end=50.0, step=0.1)
def visualize_clusters(clustering, filter_list: Optional[list] = None, texts: Optional[list] = None, radius: float = 0.05, alpha: float = 0.5, plot_width: int = 1000, plot_height: int = 530, output_in_notebook: bool = True, output_file_path: Optional[str] = None, palette: Union[list, str] = 'Wellcome33'): """ This function creates a plot of the clusters Args: clustering: wellcomeml.ml.TextClustering instance filter_list: list texts: A list of texts to be displayed by the hover function radius: float, default: 0.05 alpha: float, default: 0.5 plot_width: int, default: 600 plot_height: int, default: 600 output_in_notebook: bool, default: True output_file_path: str, default: 'cluster_viz.html' palette: list, default: Wellcome33 Returns: None (Prints a bokeh figure) """ # Dataframe creation reduced_points = clustering.reduced_points data = pd.DataFrame(reduced_points) data = data.rename(columns={0: 'X', 1: 'Y'}) data['cluster_id'] = clustering.cluster_ids data['cluster_id'] = data['cluster_id'].astype(str) data['Keywords'] = clustering.cluster_kws data['category'] = (filter_list if filter_list else ['All']*len(data)) # Palette setting palette = (Wellcome33 if palette == 'Wellcome33' else palette) palette = [str(x) for x in palette] well_background = str(WellcomeBackground) clusters = list(data['cluster_id']) clusters = list(map(int, clusters)) clusters_uniq = np.unique(clusters) data['colors'] = [(palette[x % len(palette)] if x != -1 else str(WellcomeNoData)) for x in clusters] tools = ('hover, pan, wheel_zoom, zoom_in, zoom_out, reset, save, tap') tooltips = [("index", "$index"), ("(x,y)", "($x, $y)"), ("cluster", "@cluster_id"), ("keywords", "@Keywords")] if texts is not None: # Only gets the 60 characters of the text data['text'] = [text[:60] + '...' for text in texts] tooltips += [("text", "@text")] # DropDown Button dropdown_options = list(set([('All', 'All'), None] + [(cat, cat) for i, cat in enumerate(sorted( data['category'].unique()), 2)])) dropdown = Dropdown(label='Category', button_type='default', menu=dropdown_options, width=190, align="end") # Defines figure for plotting the clusters p = figure(title="Cluster visualization", toolbar_location="above", plot_width=plot_width, plot_height=plot_height, tools=tools, tooltips=tooltips, background_fill_color=well_background) R = [] sources = [] filtered_sources = [] for x in clusters_uniq: data_cluster_id_unfiltered = data[data['cluster_id'] == str(x)] sources.append(ColumnDataSource(data_cluster_id_unfiltered)) filtered_sources.append(ColumnDataSource(data_cluster_id_unfiltered)) # Plots the cluster r = p.circle(x="X", y="Y", radius=radius, fill_alpha=alpha, color="colors", source=filtered_sources[-1]) R += [r] # JavaScript callback for the Dropdown Button callback = CustomJS( args=dict(sources=sources, filtered_sources=filtered_sources), code=""" var data = [] var cat = cb_obj.item; function generateNewDataObject(oldDataObject){ var newDataObject = {} for (var key of Object.keys(oldDataObject)){ newDataObject[key] = []; } return newDataObject } function addRowToAccumulator(accumulator, dataObject, index) { for (var key of Object.keys(dataObject)){ accumulator[key][index] = dataObject[key][index]; } return accumulator; } if (cat === 'All') { for (var i = 0; i < sources.length; i++) { data.push(sources[i].data); } } else { for (var i = 0; i < sources.length; i++) { let new_data = generateNewDataObject(sources[i].data); for (var j = 0; j <= sources[i].data['category'].length; j++) { if (sources[i].data['category'][j] == cat) { new_data = addRowToAccumulator(new_data, sources[i].data, j); } } data[i] = new_data } } for (var i = 0; i < sources.length; i++) { filtered_sources[i].data = data[i] filtered_sources[i].change.emit() } """ ) dropdown.js_on_event(MenuItemClick, callback) # Plots the legend on two columns if len(clusters_uniq) > 36: median = len(R) // 2 legend1 = Legend(items=[(str(s), [r]) for s, r in zip(clusters_uniq[:median], R[:median])]) legend2 = Legend(items=[(str(s), [r]) for s, r in zip(clusters_uniq[median:], R[median:])]) p.add_layout(legend1, 'right') p.add_layout(legend2, 'right') else: legend = Legend(items=[(str(s), [r]) for s, r in zip(clusters_uniq, R)]) p.add_layout(legend, 'right') # Plots other extra annotations to the plot p.legend.title = "Cluster ID" p.legend.label_text_font_size = "11px" p.legend.background_fill_color = str(WellcomeBackground) p.legend.click_policy = "hide" p.min_border_left = 200 # Output in notebook and new page reset_output() if output_in_notebook: output_notebook() if output_file_path: output_file(output_file_path) show(column(dropdown, p))
dwell_unloop_point_view = CDSView(source=dwell_unloop_point, filters=[dwell_unloop_filter]) loop_point_view = CDSView(source=loop_point, filters=[loop_filter]) loop_endog_view = CDSView(source=loop_endog, filters=[endog_filter]) post_endog_view = CDSView(source=post_endog, filters=[endog_filter]) post_point_view = CDSView(source=post_point) # Define the dropdown for the interactivity menu_dict = {m: m for m in mut_df['mutant'].unique() if m != 'V10-95'} menu_dict['DFL161'] = "DFL16.1-5" menu_dict['DFL1613'] = "DFL16.1-3" menu = [(v, k) for k, v in menu_dict.items() if k in ['V1-135', 'V8-18', 'V5-43']] sel = Dropdown(value='', label='Select Sequence Combination', menu=menu) # Define the figure canvas # Define the figure canvases seq_ax = bokeh.plotting.figure(width=600, height=50, x_range=[0, 30], y_range=[-0.01, 0.1], tools=[''], toolbar_location=None) dwell_all_ax = bokeh.plotting.figure( width=275, height=250, x_axis_type='log', x_range=[0.5, 80],
def create(palm): fit_max = 1 fit_min = 0 doc = curdoc() # THz calibration plot scan_plot = Plot( title=Title(text="THz calibration"), x_range=DataRange1d(), y_range=DataRange1d(), plot_height=PLOT_CANVAS_HEIGHT, plot_width=PLOT_CANVAS_WIDTH, toolbar_location="right", ) # ---- tools scan_plot.toolbar.logo = None scan_plot.add_tools(PanTool(), BoxZoomTool(), WheelZoomTool(), ResetTool()) # ---- axes scan_plot.add_layout(LinearAxis(axis_label="Stage delay motor"), place="below") scan_plot.add_layout(LinearAxis(axis_label="Energy shift, eV", major_label_orientation="vertical"), place="left") # ---- grid lines scan_plot.add_layout(Grid(dimension=0, ticker=BasicTicker())) scan_plot.add_layout(Grid(dimension=1, ticker=BasicTicker())) # ---- circle cluster glyphs scan_circle_source = ColumnDataSource(dict(x=[], y=[])) scan_plot.add_glyph(scan_circle_source, Circle(x="x", y="y", line_alpha=0, fill_alpha=0.5)) # ---- circle glyphs scan_avg_circle_source = ColumnDataSource(dict(x=[], y=[])) scan_plot.add_glyph( scan_avg_circle_source, Circle(x="x", y="y", line_color="purple", fill_color="purple")) # ---- line glyphs fit_line_source = ColumnDataSource(dict(x=[], y=[])) scan_plot.add_glyph(fit_line_source, Line(x="x", y="y", line_color="purple")) # THz calibration folder path text input def path_textinput_callback(_attr, _old_value, _new_value): update_load_dropdown_menu() path_periodic_update() path_textinput = TextInput(title="THz calibration path:", value=os.path.join(os.path.expanduser("~")), width=510) path_textinput.on_change("value", path_textinput_callback) # THz calibration eco scans dropdown def scans_dropdown_callback(_attr, _old_value, new_value): scans_dropdown.label = new_value scans_dropdown = Dropdown(label="ECO scans", button_type="default", menu=[]) scans_dropdown.on_change("value", scans_dropdown_callback) # ---- eco scans periodic update def path_periodic_update(): new_menu = [] if os.path.isdir(path_textinput.value): for entry in os.scandir(path_textinput.value): if entry.is_file() and entry.name.endswith(".json"): new_menu.append((entry.name, entry.name)) scans_dropdown.menu = sorted(new_menu, reverse=True) doc.add_periodic_callback(path_periodic_update, 5000) # Calibrate button def calibrate_button_callback(): palm.calibrate_thz( path=os.path.join(path_textinput.value, scans_dropdown.value)) fit_max_spinner.value = np.ceil(palm.thz_calib_data.index.values.max()) fit_min_spinner.value = np.floor( palm.thz_calib_data.index.values.min()) update_calibration_plot() def update_calibration_plot(): scan_plot.xaxis.axis_label = f"{palm.thz_motor_name}, {palm.thz_motor_unit}" scan_circle_source.data.update( x=np.repeat(palm.thz_calib_data.index, palm.thz_calib_data["peak_shift"].apply(len)).tolist(), y=np.concatenate( palm.thz_calib_data["peak_shift"].values).tolist(), ) scan_avg_circle_source.data.update( x=palm.thz_calib_data.index.tolist(), y=palm.thz_calib_data["peak_shift_mean"].tolist()) x = np.linspace(fit_min, fit_max, 100) y = palm.thz_slope * x + palm.thz_intersect fit_line_source.data.update(x=np.round(x, decimals=5), y=np.round(y, decimals=5)) calib_const_div.text = f""" thz_slope = {palm.thz_slope} """ calibrate_button = Button(label="Calibrate THz", button_type="default", width=250) calibrate_button.on_click(calibrate_button_callback) # THz fit maximal value text input def fit_max_spinner_callback(_attr, old_value, new_value): nonlocal fit_max if new_value > fit_min: fit_max = new_value palm.calibrate_thz( path=os.path.join(path_textinput.value, scans_dropdown.value), fit_range=(fit_min, fit_max), ) update_calibration_plot() else: fit_max_spinner.value = old_value fit_max_spinner = Spinner(title="Maximal fit value:", value=fit_max, step=0.1) fit_max_spinner.on_change("value", fit_max_spinner_callback) # THz fit maximal value text input def fit_min_spinner_callback(_attr, old_value, new_value): nonlocal fit_min if new_value < fit_max: fit_min = new_value palm.calibrate_thz( path=os.path.join(path_textinput.value, scans_dropdown.value), fit_range=(fit_min, fit_max), ) update_calibration_plot() else: fit_min_spinner.value = old_value fit_min_spinner = Spinner(title="Minimal fit value:", value=fit_min, step=0.1) fit_min_spinner.on_change("value", fit_min_spinner_callback) # Save calibration button def save_button_callback(): palm.save_thz_calib(path=path_textinput.value) update_load_dropdown_menu() save_button = Button(label="Save", button_type="default", width=250) save_button.on_click(save_button_callback) # Load calibration button def load_dropdown_callback(_attr, _old_value, new_value): palm.load_thz_calib(os.path.join(path_textinput.value, new_value)) update_calibration_plot() def update_load_dropdown_menu(): new_menu = [] calib_file_ext = ".palm_thz" if os.path.isdir(path_textinput.value): for entry in os.scandir(path_textinput.value): if entry.is_file() and entry.name.endswith((calib_file_ext)): new_menu.append( (entry.name[:-len(calib_file_ext)], entry.name)) load_dropdown.button_type = "default" load_dropdown.menu = sorted(new_menu, reverse=True) else: load_dropdown.button_type = "danger" load_dropdown.menu = new_menu doc.add_next_tick_callback(update_load_dropdown_menu) doc.add_periodic_callback(update_load_dropdown_menu, 5000) load_dropdown = Dropdown(label="Load", menu=[], width=250) load_dropdown.on_change("value", load_dropdown_callback) # Calibration constants calib_const_div = Div(text=f""" thz_slope = {0} """) # assemble tab_layout = column( row( scan_plot, Spacer(width=30), column( path_textinput, scans_dropdown, calibrate_button, fit_max_spinner, fit_min_spinner, row(save_button, load_dropdown), calib_const_div, ), )) return Panel(child=tab_layout, title="THz Calibration")
def geoplot(gdf_in, figure=None, figsize=None, title="", xlabel="Longitude", ylabel="Latitude", xlim=None, ylim=None, color="blue", colormap=None, colormap_uselog=False, colormap_range=None, category=None, dropdown=None, slider=None, slider_range=None, slider_name="", show_colorbar=True, colorbar_tick_format=None, xrange=None, yrange=None, hovertool=True, hovertool_columns=[], hovertool_string=None, simplify_shapes=None, tile_provider="CARTODBPOSITRON_RETINA", tile_provider_url=None, tile_attribution="", tile_alpha=1, panning=True, zooming=True, toolbar_location="right", show_figure=True, return_figure=True, return_html=False, legend=True, webgl=True, **kwargs): """Doc-String: TODO""" # Imports: import bokeh.plotting from bokeh.plotting import show from bokeh.models import ( HoverTool, LogColorMapper, LinearColorMapper, GeoJSONDataSource, WheelZoomTool, ColorBar, BasicTicker, LogTicker, Dropdown, Slider, ColumnDataSource, ) from bokeh.models.callbacks import CustomJS from bokeh.models.widgets import Dropdown from bokeh.palettes import all_palettes from bokeh.layouts import row, column # Make a copy of the input geodataframe: gdf = gdf_in.copy() # Check layertypes: if type(gdf) != pd.DataFrame: layertypes = [] if "Point" in str(gdf.geom_type.unique()): layertypes.append("Point") if "Line" in str(gdf.geom_type.unique()): layertypes.append("Line") if "Polygon" in str(gdf.geom_type.unique()): layertypes.append("Polygon") if len(layertypes) > 1: raise Exception( "Can only plot GeoDataFrames/Series with single type of geometry (either Point, Line or Polygon). Provided is a GeoDataFrame/Series with types: %s" % layertypes) else: layertypes = ["Point"] # Get and check provided parameters for geoplot: figure_options = { "title": title, "x_axis_label": xlabel, "y_axis_label": ylabel, "plot_width": 600, "plot_height": 400, "toolbar_location": toolbar_location, "active_scroll": "wheel_zoom", "x_axis_type": "mercator", "y_axis_type": "mercator", } if not figsize is None: width, height = figsize figure_options["plot_width"] = width figure_options["plot_height"] = height if webgl: figure_options["output_backend"] = "webgl" if type(gdf) != pd.DataFrame: # Convert GeoDataFrame to Web Mercator Projection: gdf = gdf.to_crs({"init": "epsg:3857"}) # Simplify shapes if wanted: if isinstance(simplify_shapes, numbers.Number): if layertypes[0] in ["Line", "Polygon"]: gdf["geometry"] = gdf["geometry"].simplify(simplify_shapes) elif not simplify_shapes is None: raise ValueError( "<simplify_shapes> parameter only accepts numbers or None.") # Check for category, dropdown or slider (choropleth map column): category_options = 0 if not category is None: category_options += 1 category_columns = [category] if not dropdown is None: category_options += 1 category_columns = dropdown if not slider is None: category_options += 1 category_columns = slider if category_options > 1: raise ValueError( "Only one of <category>, <dropdown> or <slider> parameters is allowed to be used at once." ) # Check for category (single choropleth plot): if category is None: pass elif isinstance(category, (list, tuple)): raise ValueError( "For <category>, please provide an existing single column of the GeoDataFrame." ) elif category in gdf.columns: pass else: raise ValueError( "Could not find column '%s' in GeoDataFrame. For <category>, please provide an existing single column of the GeoDataFrame." % category) # Check for dropdown (multiple choropleth plots via dropdown selection): if dropdown is None: pass elif not isinstance(dropdown, (list, tuple)): raise ValueError( "For <dropdown>, please provide a list/tuple of existing columns of the GeoDataFrame." ) else: for col in dropdown: if col not in gdf.columns: raise ValueError( "Could not find column '%s' for <dropdown> in GeoDataFrame. " % col) # Check for slider (multiple choropleth plots via slider selection): if slider is None: pass elif not isinstance(slider, (list, tuple)): raise ValueError( "For <slider>, please provide a list/tuple of existing columns of the GeoDataFrame." ) else: for col in slider: if col not in gdf.columns: raise ValueError( "Could not find column '%s' for <slider> in GeoDataFrame. " % col) if not slider_range is None: if not isinstance(slider_range, Iterable): raise ValueError( "<slider_range> has to be a type that is iterable like list, tuple, range, ..." ) else: slider_range = list(slider_range) if len(slider_range) != len(slider): raise ValueError( "The number of elements in <slider_range> has to be the same as in <slider>." ) steps = [] for i in range(len(slider_range) - 1): steps.append(slider_range[i + 1] - slider_range[i]) if len(set(steps)) > 1: raise ValueError( "<slider_range> has to have equal step size between each elements (like a range-object)." ) else: slider_step = steps[0] slider_start = slider_range[0] slider_end = slider_range[-1] # Check colormap if either <category>, <dropdown> or <slider> is choosen: if category_options == 1: if colormap is None: colormap = blue_colormap elif isinstance(colormap, (tuple, list)): if len(colormap) > 1: pass else: raise ValueError( "<colormap> only accepts a list/tuple of at least two colors or the name of one of the following predefined colormaps (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s" % (list(all_palettes.keys()))) elif isinstance(colormap, str): if colormap in all_palettes: colormap = all_palettes[colormap] colormap = colormap[max(colormap.keys())] else: raise ValueError( "Could not find <colormap> with name %s. The following predefined colormaps are supported (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s" % (colormap, list(all_palettes.keys()))) else: raise ValueError( "<colormap> only accepts a list/tuple of at least two colors or the name of one of the following predefined colormaps (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s" % (list(all_palettes.keys()))) else: if isinstance(color, str): colormap = [color] elif color is None: colormap = ["blue"] else: raise ValueError( "<color> has to be a string specifying the fill_color of the map glyph." ) # Check xlim & ylim: if xlim is not None: if isinstance(xlim, (tuple, list)): if len(xlim) == 2: from pyproj import Proj, transform inProj = Proj(init="epsg:4326") outProj = Proj(init="epsg:3857") xmin, xmax = xlim for _ in [xmin, xmax]: if not -180 < _ <= 180: raise ValueError( "Limits for x-axis (=Longitude) have to be between -180 and 180." ) if not xmin < xmax: raise ValueError("xmin has to be smaller than xmax.") xmin = transform(inProj, outProj, xmin, 0)[0] xmax = transform(inProj, outProj, xmax, 0)[0] figure_options["x_range"] = (xmin, xmax) else: raise ValueError( "Limits for x-axis (=Longitude) have to be of form [xmin, xmax] with values between -180 and 180." ) else: raise ValueError( "Limits for x-axis (=Longitude) have to be of form [xmin, xmax] with values between -180 and 180." ) if ylim is not None: if isinstance(ylim, (tuple, list)): if len(ylim) == 2: from pyproj import Proj, transform inProj = Proj(init="epsg:4326") outProj = Proj(init="epsg:3857") ymin, ymax = ylim for _ in [ymin, ymax]: if not -90 < _ <= 90: raise ValueError( "Limits for y-axis (=Latitude) have to be between -90 and 90." ) if not ymin < ymax: raise ValueError("ymin has to be smaller than ymax.") ymin = transform(inProj, outProj, 0, ymin)[1] ymax = transform(inProj, outProj, 0, ymax)[1] figure_options["y_range"] = (ymin, ymax) else: raise ValueError( "Limits for y-axis (=Latitude) have to be of form [ymin, ymax] with values between -90 and 90." ) else: raise ValueError( "Limits for y-axis (=Latitude) have to be of form [ymin, ymax] with values between -90 and 90." ) # Create Figure to draw: old_layout = None if figure is None: p = bokeh.plotting.figure(**figure_options) # Add Tile Source as Background: p = _add_backgroundtile(p, tile_provider, tile_provider_url, tile_attribution, tile_alpha) elif isinstance(figure, type(bokeh.plotting.figure())): p = figure elif isinstance(figure, type(column())): old_layout = figure p = _get_figure(old_layout) else: raise ValueError( "Parameter <figure> has to be of type bokeh.plotting.figure or bokeh.layouts.column." ) # Get ridd of zoom on axes: for t in p.tools: if type(t) == WheelZoomTool: t.zoom_on_axis = False # Hide legend if wanted: legend_input = legend if isinstance(legend, str): pass else: legend = "GeoLayer" # Define colormapper: if len(colormap) == 1: kwargs["fill_color"] = colormap[0] elif not category is None: # Check if category column is numerical: if not issubclass(gdf[category].dtype.type, np.number): raise NotImplementedError( "<category> plot only yet implemented for numerical columns. Column '%s' is not numerical." % category) field = category colormapper_options = {"palette": colormap} if not colormap_range is None: if not isinstance(colormap_range, (tuple, list)): raise ValueError( "<colormap_range> can only be 'None' or a tuple/list of form (min, max)." ) elif len(colormap_range) == 2: colormapper_options["low"] = colormap_range[0] colormapper_options["high"] = colormap_range[1] else: colormapper_options["low"] = gdf[field].min() colormapper_options["high"] = gdf[field].max() if colormap_uselog: colormapper = LogColorMapper(**colormapper_options) else: colormapper = LinearColorMapper(**colormapper_options) kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper} if not isinstance(legend, str): legend = str(field) elif not dropdown is None: # Check if all columns in dropdown selection are numerical: for col in dropdown: if not issubclass(gdf[col].dtype.type, np.number): raise NotImplementedError( "<dropdown> plot only yet implemented for numerical columns. Column '%s' is not numerical." % col) field = dropdown[0] colormapper_options = {"palette": colormap} if not colormap_range is None: if not isinstance(colormap_range, (tuple, list)): raise ValueError( "<colormap_range> can only be 'None' or a tuple/list of form (min, max)." ) elif len(colormap_range) == 2: colormapper_options["low"] = colormap_range[0] colormapper_options["high"] = colormap_range[1] else: colormapper_options["low"] = gdf[dropdown].min().min() colormapper_options["high"] = gdf[dropdown].max().max() if colormap_uselog: colormapper = LogColorMapper(**colormapper_options) else: colormapper = LinearColorMapper(**colormapper_options) kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper} legend = " " + field elif not slider is None: # Check if all columns in dropdown selection are numerical: for col in slider: if not issubclass(gdf[col].dtype.type, np.number): raise NotImplementedError( "<slider> plot only yet implemented for numerical columns. Column '%s' is not numerical." % col) field = slider[0] colormapper_options = {"palette": colormap} if not colormap_range is None: if not isinstance(colormap_range, (tuple, list)): raise ValueError( "<colormap_range> can only be 'None' or a tuple/list of form (min, max)." ) elif len(colormap_range) == 2: colormapper_options["low"] = colormap_range[0] colormapper_options["high"] = colormap_range[1] else: colormapper_options["low"] = gdf[slider].min().min() colormapper_options["high"] = gdf[slider].max().max() if colormap_uselog: colormapper = LogColorMapper(**colormapper_options) else: colormapper = LinearColorMapper(**colormapper_options) kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper} if not isinstance(legend, str): legend = "Geolayer" # Check that only hovertool_columns or hovertool_string is used: if isinstance(hovertool_columns, (list, tuple, str)): if len(hovertool_columns) > 0 and hovertool_string is not None: raise ValueError( "Either <hovertool_columns> or <hovertool_string> can be used, but not both at the same time." ) else: raise ValueError( "<hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'." ) if hovertool_string is not None: hovertool_columns = "all" # Check for Hovertool columns: if hovertool: if not isinstance(hovertool_columns, (list, tuple)): if hovertool_columns == "all": hovertool_columns = list( filter(lambda col: col != "geometry", gdf.columns)) else: raise ValueError( "<hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'." ) elif len(hovertool_columns) == 0: if not category is None: hovertool_columns = [category] elif not dropdown is None: hovertool_columns = dropdown elif not slider is None: hovertool_columns = slider else: hovertool_columns = [] else: for col in hovertool_columns: if col not in gdf.columns: raise ValueError( "Could not find columns '%s' in GeoDataFrame. <hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'." % col) else: if category is None: hovertool_columns = [] else: hovertool_columns = [category] # Reduce DataFrame to needed columns: if type(gdf) == pd.DataFrame: gdf["Geometry"] = 0 additional_columns = ["x", "y"] else: additional_columns = ["geometry"] for kwarg, value in kwargs.items(): if isinstance(value, Hashable): if value in gdf.columns: additional_columns.append(value) if category_options == 0: gdf = gdf[list(set(hovertool_columns) | set(additional_columns))] else: gdf = gdf[list( set(hovertool_columns) | set(category_columns) | set(additional_columns))] gdf["Colormap"] = gdf[field] field = "Colormap" # Create GeoJSON DataSource for Plot: if type(gdf) != pd.DataFrame: geo_source = GeoJSONDataSource(geojson=gdf.to_json()) else: geo_source = gdf # Draw Glyph on Figure: layout = None if "Point" in layertypes: if "line_color" not in kwargs: kwargs["line_color"] = kwargs["fill_color"] glyph = p.scatter(x="x", y="y", source=geo_source, legend=legend, **kwargs) if "Line" in layertypes: if "line_color" not in kwargs: kwargs["line_color"] = kwargs["fill_color"] del kwargs["fill_color"] glyph = p.multi_line(xs="xs", ys="ys", source=geo_source, legend=legend, **kwargs) if "Polygon" in layertypes: if "line_color" not in kwargs: kwargs["line_color"] = "black" # Creates from a geoDataFrame with Polygons and Multipolygons a Pandas DataFrame # with x any y columns specifying the geometry of the Polygons: geo_source = ColumnDataSource(convert_geoDataFrame_to_patches(gdf)) # Plot polygons: glyph = p.multi_polygons(xs="__x__", ys="__y__", source=geo_source, legend=legend, **kwargs) # Add hovertool: if hovertool and (category_options == 1 or len(hovertool_columns) > 0): my_hover = HoverTool(renderers=[glyph]) if hovertool_string is None: my_hover.tooltips = [(str(col), "@{%s}" % col) for col in hovertool_columns] else: my_hover.tooltips = hovertool_string p.add_tools(my_hover) # Add colorbar: if show_colorbar and category_options == 1: colorbar_options = { "color_mapper": colormapper, "label_standoff": 12, "border_line_color": None, "location": (0, 0), } if colormap_uselog: colorbar_options["ticker"] = LogTicker() if colorbar_tick_format: colorbar_options["formatter"] = get_tick_formatter( colorbar_tick_format) colorbar = ColorBar(**colorbar_options) p.add_layout(colorbar, "right") # Add Dropdown Widget: if not dropdown is None: # Define Dropdown widget: dropdown_widget = Dropdown(label="Select Choropleth Layer", menu=list(zip(dropdown, dropdown))) # Define Callback for Dropdown widget: callback = CustomJS( args=dict( dropdown_widget=dropdown_widget, geo_source=geo_source, legend=p.legend[0].items[0], ), code=""" //Change selection of field for Colormapper for choropleth plot: geo_source.data["Colormap"] = geo_source.data[dropdown_widget.value]; geo_source.change.emit(); //Change label of Legend: legend.label["value"] = " " + dropdown_widget.value; """, ) dropdown_widget.js_on_change("value", callback) # Add Dropdown widget above the plot: if old_layout is None: layout = column(dropdown_widget, p) else: layout = column(dropdown_widget, old_layout) # Add Slider Widget: if not slider is None: if slider_range is None: slider_start = 0 slider_end = len(slider) - 1 slider_step = 1 value2name = ColumnDataSource({ "Values": np.arange(slider_start, slider_end + slider_step, slider_step), "Names": slider, }) # Define Slider widget: slider_widget = Slider( start=slider_start, end=slider_end, value=slider_start, step=slider_step, title=slider_name, ) # Define Callback for Slider widget: callback = CustomJS( args=dict( slider_widget=slider_widget, geo_source=geo_source, value2name=value2name, ), code=""" //Change selection of field for Colormapper for choropleth plot: var slider_value = slider_widget.value; for(i=0; i<value2name.data["Names"].length; i++) { if (value2name.data["Values"][i] == slider_value) { var name = value2name.data["Names"][i]; } } geo_source.data["Colormap"] = geo_source.data[name]; geo_source.change.emit(); """, ) slider_widget.js_on_change("value", callback) # Add Slider widget above the plot: if old_layout is None: layout = column(slider_widget, p) else: layout = column(slider_widget, old_layout) # Hide legend if user wants: if legend_input is False: p.legend.visible = False # Set click policy for legend: p.legend.click_policy = "hide" # Set panning option: if panning is False: p.toolbar.active_drag = None # Set zooming option: if zooming is False: p.toolbar.active_scroll = None # Display plot and if wanted return plot: if layout is None: if old_layout is None: layout = p else: layout = old_layout # Display plot if wanted if show_figure: show(layout) # Return as (embeddable) HTML if wanted: if return_html: return embedded_html(layout) # Return plot: if return_figure: return layout
def create(palm): energy_min = palm.energy_range.min() energy_max = palm.energy_range.max() energy_npoints = palm.energy_range.size current_results = (0, 0, 0, 0) doc = curdoc() # Streaked and reference waveforms plot waveform_plot = Plot( title=Title(text="eTOF waveforms"), x_range=DataRange1d(), y_range=DataRange1d(), plot_height=PLOT_CANVAS_HEIGHT, plot_width=PLOT_CANVAS_WIDTH, toolbar_location='right', ) # ---- tools waveform_plot.toolbar.logo = None waveform_plot.add_tools(PanTool(), BoxZoomTool(), WheelZoomTool(), ResetTool()) # ---- axes waveform_plot.add_layout(LinearAxis(axis_label='Photon energy, eV'), place='below') waveform_plot.add_layout(LinearAxis(axis_label='Intensity', major_label_orientation='vertical'), place='left') # ---- grid lines waveform_plot.add_layout(Grid(dimension=0, ticker=BasicTicker())) waveform_plot.add_layout(Grid(dimension=1, ticker=BasicTicker())) # ---- line glyphs waveform_source = ColumnDataSource( dict(x_str=[], y_str=[], x_ref=[], y_ref=[])) waveform_ref_line = waveform_plot.add_glyph( waveform_source, Line(x='x_ref', y='y_ref', line_color='blue')) waveform_str_line = waveform_plot.add_glyph( waveform_source, Line(x='x_str', y='y_str', line_color='red')) # ---- legend waveform_plot.add_layout( Legend(items=[("reference", [waveform_ref_line]), ("streaked", [waveform_str_line])])) waveform_plot.legend.click_policy = "hide" # Cross-correlation plot xcorr_plot = Plot( title=Title(text="Waveforms cross-correlation"), x_range=DataRange1d(), y_range=DataRange1d(), plot_height=PLOT_CANVAS_HEIGHT, plot_width=PLOT_CANVAS_WIDTH, toolbar_location='right', ) # ---- tools xcorr_plot.toolbar.logo = None xcorr_plot.add_tools(PanTool(), BoxZoomTool(), WheelZoomTool(), ResetTool()) # ---- axes xcorr_plot.add_layout(LinearAxis(axis_label='Energy shift, eV'), place='below') xcorr_plot.add_layout(LinearAxis(axis_label='Cross-correlation', major_label_orientation='vertical'), place='left') # ---- grid lines xcorr_plot.add_layout(Grid(dimension=0, ticker=BasicTicker())) xcorr_plot.add_layout(Grid(dimension=1, ticker=BasicTicker())) # ---- line glyphs xcorr_source = ColumnDataSource(dict(lags=[], xcorr1=[], xcorr2=[])) xcorr_plot.add_glyph( xcorr_source, Line(x='lags', y='xcorr1', line_color='purple', line_dash='dashed')) xcorr_plot.add_glyph(xcorr_source, Line(x='lags', y='xcorr2', line_color='purple')) # ---- vertical span xcorr_center_span = Span(location=0, dimension='height') xcorr_plot.add_layout(xcorr_center_span) # Delays plot pulse_delay_plot = Plot( title=Title(text="Pulse delays"), x_range=DataRange1d(), y_range=DataRange1d(), plot_height=PLOT_CANVAS_HEIGHT, plot_width=PLOT_CANVAS_WIDTH, toolbar_location='right', ) # ---- tools pulse_delay_plot.toolbar.logo = None pulse_delay_plot.add_tools(PanTool(), BoxZoomTool(), WheelZoomTool(), ResetTool()) # ---- axes pulse_delay_plot.add_layout(LinearAxis(axis_label='Pulse number'), place='below') pulse_delay_plot.add_layout( LinearAxis(axis_label='Pulse delay (uncalib), eV', major_label_orientation='vertical'), place='left', ) # ---- grid lines pulse_delay_plot.add_layout(Grid(dimension=0, ticker=BasicTicker())) pulse_delay_plot.add_layout(Grid(dimension=1, ticker=BasicTicker())) # ---- line glyphs pulse_delay_source = ColumnDataSource(dict(pulse=[], delay=[])) pulse_delay_plot.add_glyph( pulse_delay_source, Line(x='pulse', y='delay', line_color='steelblue')) # ---- vertical span pulse_delay_plot_span = Span(location=0, dimension='height') pulse_delay_plot.add_layout(pulse_delay_plot_span) # Pulse lengths plot pulse_length_plot = Plot( title=Title(text="Pulse lengths"), x_range=DataRange1d(), y_range=DataRange1d(), plot_height=PLOT_CANVAS_HEIGHT, plot_width=PLOT_CANVAS_WIDTH, toolbar_location='right', ) # ---- tools pulse_length_plot.toolbar.logo = None pulse_length_plot.add_tools(PanTool(), BoxZoomTool(), WheelZoomTool(), ResetTool()) # ---- axes pulse_length_plot.add_layout(LinearAxis(axis_label='Pulse number'), place='below') pulse_length_plot.add_layout( LinearAxis(axis_label='Pulse length (uncalib), eV', major_label_orientation='vertical'), place='left', ) # ---- grid lines pulse_length_plot.add_layout(Grid(dimension=0, ticker=BasicTicker())) pulse_length_plot.add_layout(Grid(dimension=1, ticker=BasicTicker())) # ---- line glyphs pulse_length_source = ColumnDataSource(dict(x=[], y=[])) pulse_length_plot.add_glyph(pulse_length_source, Line(x='x', y='y', line_color='steelblue')) # ---- vertical span pulse_length_plot_span = Span(location=0, dimension='height') pulse_length_plot.add_layout(pulse_length_plot_span) # Folder path text input def path_textinput_callback(_attr, _old, new): save_textinput.value = new path_periodic_update() path_textinput = TextInput(title="Folder Path:", value=os.path.join(os.path.expanduser('~')), width=510) path_textinput.on_change('value', path_textinput_callback) # Saved runs dropdown menu def h5_update(pulse, delays, debug_data): prep_data, lags, corr_res_uncut, corr_results = debug_data waveform_source.data.update( x_str=palm.energy_range, y_str=prep_data['1'][pulse, :], x_ref=palm.energy_range, y_ref=prep_data['0'][pulse, :], ) xcorr_source.data.update(lags=lags, xcorr1=corr_res_uncut[pulse, :], xcorr2=corr_results[pulse, :]) xcorr_center_span.location = delays[pulse] pulse_delay_plot_span.location = pulse pulse_length_plot_span.location = pulse # this placeholder function should be reassigned in 'saved_runs_dropdown_callback' h5_update_fun = lambda pulse: None def saved_runs_dropdown_callback(_attr, _old, new): if new != "Saved Runs": nonlocal h5_update_fun, current_results saved_runs_dropdown.label = new filepath = os.path.join(path_textinput.value, new) tags, delays, lengths, debug_data = palm.process_hdf5_file( filepath, debug=True) current_results = (new, tags, delays, lengths) if autosave_checkbox.active: save_button_callback() pulse_delay_source.data.update(pulse=np.arange(len(delays)), delay=delays) pulse_length_source.data.update(x=np.arange(len(lengths)), y=lengths) h5_update_fun = partial(h5_update, delays=delays, debug_data=debug_data) pulse_slider.end = len(delays) - 1 pulse_slider.value = 0 h5_update_fun(0) saved_runs_dropdown = Dropdown(label="Saved Runs", button_type='primary', menu=[]) saved_runs_dropdown.on_change('value', saved_runs_dropdown_callback) # ---- saved run periodic update def path_periodic_update(): new_menu = [] if os.path.isdir(path_textinput.value): for entry in os.scandir(path_textinput.value): if entry.is_file() and entry.name.endswith(('.hdf5', '.h5')): new_menu.append((entry.name, entry.name)) saved_runs_dropdown.menu = sorted(new_menu, reverse=True) doc.add_periodic_callback(path_periodic_update, 5000) # Pulse number slider def pulse_slider_callback(_attr, _old, new): h5_update_fun(pulse=new) pulse_slider = Slider( start=0, end=99999, value=0, step=1, title="Pulse ID", callback_policy='throttle', callback_throttle=500, ) pulse_slider.on_change('value', pulse_slider_callback) # Energy maximal range value text input def energy_max_textinput_callback(_attr, old, new): nonlocal energy_max try: new_value = float(new) if new_value > energy_min: energy_max = new_value palm.energy_range = np.linspace(energy_min, energy_max, energy_npoints) saved_runs_dropdown_callback('', '', saved_runs_dropdown.label) else: energy_max_textinput.value = old except ValueError: energy_max_textinput.value = old energy_max_textinput = TextInput(title='Maximal Energy, eV:', value=str(energy_max)) energy_max_textinput.on_change('value', energy_max_textinput_callback) # Energy minimal range value text input def energy_min_textinput_callback(_attr, old, new): nonlocal energy_min try: new_value = float(new) if new_value < energy_max: energy_min = new_value palm.energy_range = np.linspace(energy_min, energy_max, energy_npoints) saved_runs_dropdown_callback('', '', saved_runs_dropdown.label) else: energy_min_textinput.value = old except ValueError: energy_min_textinput.value = old energy_min_textinput = TextInput(title='Minimal Energy, eV:', value=str(energy_min)) energy_min_textinput.on_change('value', energy_min_textinput_callback) # Energy number of interpolation points text input def energy_npoints_textinput_callback(_attr, old, new): nonlocal energy_npoints try: new_value = int(new) if new_value > 1: energy_npoints = new_value palm.energy_range = np.linspace(energy_min, energy_max, energy_npoints) saved_runs_dropdown_callback('', '', saved_runs_dropdown.label) else: energy_npoints_textinput.value = old except ValueError: energy_npoints_textinput.value = old energy_npoints_textinput = TextInput( title='Number of interpolation points:', value=str(energy_npoints)) energy_npoints_textinput.on_change('value', energy_npoints_textinput_callback) # Save location save_textinput = TextInput(title="Save Folder Path:", value=os.path.join(os.path.expanduser('~'))) # Autosave checkbox autosave_checkbox = CheckboxButtonGroup(labels=["Auto Save"], active=[], width=250) # Save button def save_button_callback(): if current_results[0]: filename, tags, delays, lengths = current_results save_filename = os.path.splitext(filename)[0] + '.csv' df = pd.DataFrame({ 'pulse_id': tags, 'pulse_delay': delays, 'pulse_length': lengths }) df.to_csv(os.path.join(save_textinput.value, save_filename), index=False) save_button = Button(label="Save Results", button_type='default', width=250) save_button.on_click(save_button_callback) # assemble tab_layout = column( row( column(waveform_plot, xcorr_plot), Spacer(width=30), column( path_textinput, saved_runs_dropdown, pulse_slider, Spacer(height=30), energy_min_textinput, energy_max_textinput, energy_npoints_textinput, Spacer(height=30), save_textinput, autosave_checkbox, save_button, ), ), row(pulse_delay_plot, Spacer(width=10), pulse_length_plot), ) return Panel(child=tab_layout, title="HDF5 File")
def geoplot(gdf_in, fig=None, figsize=None, title="", xlabel="Longitude", ylabel="Latitude", color="blue", colormap=None, colormap_uselog=False, colormap_range=None, category=None, dropdown=None, slider=None, slider_range=None, slider_name="", show_colorbar=True, xrange=None, yrange=None, hovertool=True, hovertool_columns=[], simplify_shapes=None, tile_provider="CARTODBPOSITRON_RETINA", tile_provider_url=None, toolbar_location="right", show_figure=True, return_figure=True, return_html=False, legend=True, webgl=True, **kwargs): """Doc-String: TODO""" gdf = gdf_in.copy() # Check layertypes: layertypes = [] if "Point" in str(gdf.geom_type.unique()): layertypes.append("Point") if "Line" in str(gdf.geom_type.unique()): layertypes.append("Line") if "Polygon" in str(gdf.geom_type.unique()): layertypes.append("Polygon") if len(layertypes) > 1: raise Exception( "Can only plot GeoDataFrames/Series with single type of geometry (either Point, Line or Polygon). Provided is a GeoDataFrame/Series with types: %s" % layertypes) # Get and check provided parameters for geoplot: figure_options = { "title": title, "x_axis_label": xlabel, "y_axis_label": ylabel, "plot_width": 600, "plot_height": 400, "toolbar_location": toolbar_location, "active_scroll": "wheel_zoom", } if not isinstance(figsize, type(None)): width, height = figsize figure_options["plot_width"] = width figure_options["plot_height"] = height if webgl: figure_options["output_backend"] = "webgl" if not isinstance(fig, type(None)): raise NotImplementedError("Parameter <figure> not yet implemented.") # Convert GeoDataFrame to Web Mercador Projection: gdf.to_crs({"init": "epsg:3857"}, inplace=True) # Simplify shapes if wanted: if isinstance(simplify_shapes, numbers.Number): if layertypes[0] in ["Line", "Polygon"]: gdf["geometry"] = gdf["geometry"].simplify(simplify_shapes) elif not isinstance(simplify_shapes, type(None)): raise ValueError( "<simplify_shapes> parameter only accepts numbers or None.") # Check for category, dropdown or slider (choropleth map column): category_options = 0 if not isinstance(category, type(None)): category_options += 1 category_columns = [category] if not isinstance(dropdown, type(None)): category_options += 1 category_columns = dropdown if not isinstance(slider, type(None)): category_options += 1 category_columns = slider if category_options > 1: raise ValueError( "Only one of <category>, <dropdown> or <slider> parameters is allowed to be used at once." ) # Check for category (single choropleth plot): if isinstance(category, type(None)): pass elif isinstance(category, (list, tuple)): raise ValueError( "For <category>, please provide an existing single column of the GeoDataFrame." ) elif category in gdf.columns: pass else: raise ValueError( "Could not find column '%s' in GeoDataFrame. For <category>, please provide an existing single column of the GeoDataFrame." % category) # Check for dropdown (multiple choropleth plots via dropdown selection): if isinstance(dropdown, type(None)): pass elif not isinstance(dropdown, (list, tuple)): raise ValueError( "For <dropdown>, please provide a list/tuple of existing columns of the GeoDataFrame." ) else: for col in dropdown: if col not in gdf.columns: raise ValueError( "Could not find column '%s' for <dropdown> in GeoDataFrame. " % col) # Check for slider (multiple choropleth plots via slider selection): if isinstance(slider, type(None)): pass elif not isinstance(slider, (list, tuple)): raise ValueError( "For <slider>, please provide a list/tuple of existing columns of the GeoDataFrame." ) else: for col in slider: if col not in gdf.columns: raise ValueError( "Could not find column '%s' for <slider> in GeoDataFrame. " % col) if not isinstance(slider_range, type(None)): if not isinstance(slider_range, Iterable): raise ValueError( "<slider_range> has to be a type that is iterable like list, tuple, range, ..." ) else: slider_range = list(slider_range) if len(slider_range) != len(slider): raise ValueError( "The number of elements in <slider_range> has to be the same as in <slider>." ) steps = [] for i in range(len(slider_range) - 1): steps.append(slider_range[i + 1] - slider_range[i]) if len(set(steps)) > 1: raise ValueError( "<slider_range> has to have equal step size between each elements (like a range-object)." ) else: slider_step = steps[0] slider_start = slider_range[0] slider_end = slider_range[-1] # Check colormap if either <category>, <dropdown> or <slider> is choosen: if category_options == 1: if isinstance(colormap, type(None)): colormap = blue_colormap elif isinstance(colormap, (tuple, list)): if len(colormap) > 1: pass else: raise ValueError( "<colormap> only accepts a list/tuple of at least two colors or the name of one of the following predefined colormaps (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s" % (list(all_palettes.keys()))) elif isinstance(colormap, str): if colormap in all_palettes: colormap = all_palettes[colormap] colormap = colormap[max(colormap.keys())] else: raise ValueError( "Could not find <colormap> with name %s. The following predefined colormaps are supported (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s" % (colormap, list(all_palettes.keys()))) else: raise ValueError( "<colormap> only accepts a list/tuple of at least two colors or the name of one of the following predefined colormaps (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s" % (list(all_palettes.keys()))) else: if isinstance(color, str): colormap = [color] else: raise ValueError( "<color> has to be a string specifying the fill_color of the map glyph." ) # Create Figure to draw: p = figure(x_axis_type="mercator", y_axis_type="mercator", **figure_options) # Get ridd of zoom on axes: for t in p.tools: if type(t) == WheelZoomTool: t.zoom_on_axis = False # Add Tile Source as Background: p = _add_backgroundtile(p, tile_provider, tile_provider_url) # Hide legend if wanted: if not legend: p.legend.visible = False elif isinstance(legend, str): pass else: legend = "GeoLayer" # Define colormapper: if len(colormap) == 1: kwargs["fill_color"] = colormap[0] elif not isinstance(category, type(None)): # Check if category column is numerical: if not issubclass(gdf[category].dtype.type, np.number): raise NotImplementedError( "<category> plot only yet implemented for numerical columns. Column '%s' is not numerical." % category) field = category colormapper_options = {"palette": colormap} if not isinstance(colormap_range, type(None)): if not isinstance(colormap_range, (tuple, list)): raise ValueError( "<colormap_range> can only be 'None' or a tuple/list of form (min, max)." ) elif len(colormap_range) == 2: colormapper_options["low"] = colormap_range[0] colormapper_options["high"] = colormap_range[1] else: colormapper_options["low"] = gdf[field].min() colormapper_options["high"] = gdf[field].max() if colormap_uselog: colormapper = LogColorMapper(**colormapper_options) else: colormapper = LinearColorMapper(**colormapper_options) kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper} if not isinstance(legend, str): legend = str(field) elif not isinstance(dropdown, type(None)): # Check if all columns in dropdown selection are numerical: for col in dropdown: if not issubclass(gdf[col].dtype.type, np.number): raise NotImplementedError( "<dropdown> plot only yet implemented for numerical columns. Column '%s' is not numerical." % col) field = dropdown[0] colormapper_options = {"palette": colormap} if not isinstance(colormap_range, type(None)): if not isinstance(colormap_range, (tuple, list)): raise ValueError( "<colormap_range> can only be 'None' or a tuple/list of form (min, max)." ) elif len(colormap_range) == 2: colormapper_options["low"] = colormap_range[0] colormapper_options["high"] = colormap_range[1] else: colormapper_options["low"] = gdf[dropdown].min().min() colormapper_options["high"] = gdf[dropdown].max().max() if colormap_uselog: colormapper = LogColorMapper(**colormapper_options) else: colormapper = LinearColorMapper(**colormapper_options) kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper} if not isinstance(legend, str): legend = "Geolayer" elif not isinstance(slider, type(None)): # Check if all columns in dropdown selection are numerical: for col in slider: if not issubclass(gdf[col].dtype.type, np.number): raise NotImplementedError( "<slider> plot only yet implemented for numerical columns. Column '%s' is not numerical." % col) field = slider[0] colormapper_options = {"palette": colormap} if not isinstance(colormap_range, type(None)): if not isinstance(colormap_range, (tuple, list)): raise ValueError( "<colormap_range> can only be 'None' or a tuple/list of form (min, max)." ) elif len(colormap_range) == 2: colormapper_options["low"] = colormap_range[0] colormapper_options["high"] = colormap_range[1] else: colormapper_options["low"] = gdf[slider].min().min() colormapper_options["high"] = gdf[slider].max().max() if colormap_uselog: colormapper = LogColorMapper(**colormapper_options) else: colormapper = LinearColorMapper(**colormapper_options) kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper} if not isinstance(legend, str): legend = "Geolayer" # Check for Hovertool columns: if hovertool: if not isinstance(hovertool_columns, (list, tuple)): if hovertool_columns == "all": hovertool_columns = list( filter(lambda col: col != "geometry", df_shapes.columns)) else: raise ValueError( "<hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'." ) elif len(hovertool_columns) == 0: if not isinstance(category, type(None)): hovertool_columns = [category] elif not isinstance(dropdown, type(None)): hovertool_columns = dropdown elif not isinstance(slider, type(None)): hovertool_columns = slider else: hovertool_columns = [] else: for col in hovertool_columns: if col not in gdf.columns: raise ValueError( "Could not find columns '%s' in GeoDataFrame. <hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'." % col) else: if isinstance(category, type(None)): hovertool_columns = [] else: hovertool_columns = [category] # Reduce DataFrame to needed columns: additional_columns = [] for kwarg, value in kwargs.items(): if isinstance(value, Hashable): if value in gdf.columns: additional_columns.append(value) if category_options == 0: gdf = gdf[list(set(hovertool_columns) | set(additional_columns)) + ["geometry"]] else: gdf = gdf[list( set(hovertool_columns) | set(category_columns) | set(additional_columns)) + ["geometry"]] gdf["Colormap"] = gdf[field] field = "Colormap" # Create GeoJSON DataSource for Plot: geo_source = GeoJSONDataSource(geojson=gdf.to_json()) # Draw Glyph on Figure: layout = None if "Point" in layertypes: if "line_color" not in kwargs: kwargs["line_color"] = kwargs["fill_color"] p.scatter(x="x", y="y", source=geo_source, legend=legend, **kwargs) if "Line" in layertypes: if "line_color" not in kwargs: kwargs["line_color"] = kwargs["fill_color"] del kwargs["fill_color"] p.multi_line(xs="xs", ys="ys", source=geo_source, legend=legend, **kwargs) if "Polygon" in layertypes: if "line_color" not in kwargs: kwargs["line_color"] = "black" # Plot polygons: p.patches(xs="xs", ys="ys", source=geo_source, legend=legend, **kwargs) if hovertool and (category_options == 1 or len(hovertool_columns) > 0): my_hover = HoverTool() my_hover.tooltips = [(str(col), "@{%s}" % col) for col in hovertool_columns] p.add_tools(my_hover) if show_colorbar and category_options == 1: colorbar_options = { "color_mapper": colormapper, "label_standoff": 12, "border_line_color": None, "location": (0, 0), } if colormap_uselog: colorbar_options["ticker"] = LogTicker() colorbar = ColorBar(**colorbar_options) p.add_layout(colorbar, "right") if not isinstance(dropdown, type(None)): # Define Dropdown widget: dropdown_widget = Dropdown(label="Select Choropleth Layer", menu=list(zip(dropdown, dropdown))) # Define Callback for Dropdown widget: callback = CustomJS( args=dict(dropdown_widget=dropdown_widget, geo_source=geo_source, p=p), code=""" //Change selection of field for Colormapper for choropleth plot: geo_source.data["Colormap"] = geo_source.data[dropdown_widget.value]; geo_source.change.emit(); //p.legend[0].items[0]["label"] = dropdown_widget.value; """, ) dropdown_widget.js_on_change("value", callback) # Add Dropdown widget above the plot: layout = column(dropdown_widget, p) if not isinstance(slider, type(None)): if slider_range is None: slider_start = 0 slider_end = len(slider) - 1 slider_step = 1 value2name = ColumnDataSource({ "Values": np.arange(slider_start, slider_end + slider_step, slider_step), "Names": slider, }) # Define Slider widget: slider_widget = Slider( start=slider_start, end=slider_end, value=slider_start, step=slider_step, title=slider_name, ) # Define Callback for Slider widget: callback = CustomJS( args=dict( slider_widget=slider_widget, geo_source=geo_source, value2name=value2name, ), code=""" //Change selection of field for Colormapper for choropleth plot: var slider_value = slider_widget.value; for(i=0; i<value2name.data["Names"].length; i++) { if (value2name.data["Values"][i] == slider_value) { var name = value2name.data["Names"][i]; } } geo_source.data["Colormap"] = geo_source.data[name]; geo_source.change.emit(); """, ) slider_widget.js_on_change("value", callback) # Add Slider widget above the plot: layout = column(slider_widget, p) # Set click policy for legend: p.legend.click_policy = "hide" # Display plot and if wanted return plot: if isinstance(layout, type(None)): layout = p # Display plot if wanted if show_figure: show(layout) # Return as (embeddable) HTML if wanted: if return_html: return embedded_html(layout) # Return plot: if return_figure: return layout
def bkapp(doc): def fitButtonCallback(new): x_range = [p.x_range.start, p.x_range.end] y_range = [p.y_range.start, p.y_range.end] x_inds = np.where(np.logical_and(bin_mids >= x_range[0], bin_mids <= x_range[1]), bin_mids, np.nan) x = x_inds[np.isfinite(x_inds)] y = hist[np.isfinite(x_inds)] x_fit = np.linspace(x_range[0], x_range[1], 10000) p0 = [y.max(), x[np.argmax(y)], x[np.argmax(y)], 0] p_fit = curve_fit(_gaus, x, y, p0, ftol=1e-2)[0] y_fit = _gaus(x_fit, *p_fit) # p.line(x_fit, y_fit, legend_label='Amp. = %5.2f, Mean = %5.2f, Std. = %5.2f, Base = %5.2f' % p_fit) fitDataSource.patch(dict(x=[(slice(0, len(x_fit)), x_fit)], y=[(slice(0, len(y_fit)), y_fit)])) fit_str = 'Amp. = %5.2f, Mean = %5.2f, Std. = %5.2f, Base = %5.2f' % (p_fit[0], p_fit[1], p_fit[2], p_fit[3]) #p.line(x=x_fit, y=y_fit, legend_label=new_data['legend_label'], color='black') p.x_range = Range1d(bins[0], bins[-1]) p.y_range = Range1d(0, hists[0].max()) p.legend.items = [(settings['sz_key'], [hist_line]), (fit_str, [fit_line])] def SzDropDownCallback(new): settings['sz_key'] = new.item bins = np.linspace(0, 130000, 1000) size_datasets = pysp2.util.calc_diams_masses(my_cal_data[settings['sz_key']]) hist, bins = np.histogram(size_datasets[settings['variable']].where(size_datasets.ScatRejectKey == 0).values, bins=bins) histDataSource.patch(dict(x=[(slice(0, len(bin_mids)), bin_mids)], y=[(slice(0, len(hist)), hist)])) p.legend.items = [(settings['sz_key'], [hist_line]), ("", [fit_line])] def VariableCallback(new): settings['variable'] = new.item bins = np.linspace(0, 130000, 1000) size_datasets = pysp2.util.calc_diams_masses(my_cal_data[settings['sz_key']]) hist, bins = np.histogram(size_datasets[settings['variable']].where(size_datasets.ScatRejectKey == 0).values, bins=bins) histDataSource.patch(dict(x=[(slice(0, len(bin_mids)), bin_mids)], y=[(slice(0, len(hist)), hist)])) p.legend.items = [(settings['sz_key'], [hist_line]), ("", [fit_line])] settings = {'sz_key':'scat_200', 'variable': 'PkHt_ch0'} sizes = np.array([350, 300, 269, 220, 200, 170]) scat_mean = np.ones_like(sizes, dtype=float) size_datasets = {} i = 0 # bins = np.logspace(-13, -10, 120) bins = np.linspace(0, 130000, 1000) hists = np.zeros((len(sizes), len(bins) - 1)) size_datasets = pysp2.util.calc_diams_masses(my_cal_data[settings['sz_key']]) hist, bins = np.histogram(size_datasets[settings['variable']].where(size_datasets.ScatRejectKey == 0).values, bins=bins) p = figure(tools="pan,box_zoom,reset,save", x_range=(bins[0], bins[-1]), y_range=(0, hist.max())) bin_mids = (bins[:-1] + bins[1:])/2.0 histDataSource = ColumnDataSource(data=dict(x=bin_mids, y=hist)) x_fit = np.linspace(0, 130000, 10000) fitDataSource = ColumnDataSource(data=dict(x=x_fit, y=np.nan*np.ones_like(x_fit))) hist_line = p.line('x', 'y', source=histDataSource, legend_label=settings['sz_key']) fit_line = p.line('x', 'y', source=fitDataSource, legend_label="") button = Button(label="Curve fit") button.on_click(fitButtonCallback) size_menu = [] for keys in my_cal_data.keys(): size_menu.append((keys, keys)) SzDropDown = Dropdown(label="Calibration data", menu=size_menu) SzDropDown.on_click(SzDropDownCallback) vars = ["PkHt_ch0", "PkHt_ch1", "PkHt_ch4", "PkHt_ch5", "FtAmp_ch0", 'FtAmp_ch4'] vars = [(x, x) for x in vars] VarDropDown = Dropdown(label="Variable", menu=vars) VarDropDown.on_click(VariableCallback) doc.add_root(column(button, SzDropDown, VarDropDown, p))
def create(): det_data = {} fit_params = {} js_data = ColumnDataSource( data=dict(content=["", ""], fname=["", ""], ext=["", ""])) def proposal_textinput_callback(_attr, _old, new): proposal = new.strip() for zebra_proposals_path in pyzebra.ZEBRA_PROPOSALS_PATHS: proposal_path = os.path.join(zebra_proposals_path, proposal) if os.path.isdir(proposal_path): # found it break else: raise ValueError(f"Can not find data for proposal '{proposal}'.") file_list = [] for file in os.listdir(proposal_path): if file.endswith((".ccl", ".dat")): file_list.append((os.path.join(proposal_path, file), file)) file_select.options = file_list file_open_button.disabled = False file_append_button.disabled = False proposal_textinput = TextInput(title="Proposal number:", width=210) proposal_textinput.on_change("value", proposal_textinput_callback) def _init_datatable(): scan_list = [s["idx"] for s in det_data] hkl = [f'{s["h"]} {s["k"]} {s["l"]}' for s in det_data] export = [s.get("active", True) for s in det_data] scan_table_source.data.update( scan=scan_list, hkl=hkl, fit=[0] * len(scan_list), export=export, ) scan_table_source.selected.indices = [] scan_table_source.selected.indices = [0] merge_options = [(str(i), f"{i} ({idx})") for i, idx in enumerate(scan_list)] merge_from_select.options = merge_options merge_from_select.value = merge_options[0][0] file_select = MultiSelect(title="Available .ccl/.dat files:", width=210, height=250) def file_open_button_callback(): nonlocal det_data det_data = [] for f_name in file_select.value: with open(f_name) as file: base, ext = os.path.splitext(f_name) if det_data: append_data = pyzebra.parse_1D(file, ext) pyzebra.normalize_dataset(append_data, monitor_spinner.value) pyzebra.merge_datasets(det_data, append_data) else: det_data = pyzebra.parse_1D(file, ext) pyzebra.normalize_dataset(det_data, monitor_spinner.value) pyzebra.merge_duplicates(det_data) js_data.data.update(fname=[base, base]) _init_datatable() append_upload_button.disabled = False file_open_button = Button(label="Open New", width=100, disabled=True) file_open_button.on_click(file_open_button_callback) def file_append_button_callback(): for f_name in file_select.value: with open(f_name) as file: _, ext = os.path.splitext(f_name) append_data = pyzebra.parse_1D(file, ext) pyzebra.normalize_dataset(append_data, monitor_spinner.value) pyzebra.merge_datasets(det_data, append_data) _init_datatable() file_append_button = Button(label="Append", width=100, disabled=True) file_append_button.on_click(file_append_button_callback) def upload_button_callback(_attr, _old, new): nonlocal det_data det_data = [] for f_str, f_name in zip(new, upload_button.filename): with io.StringIO(base64.b64decode(f_str).decode()) as file: base, ext = os.path.splitext(f_name) if det_data: append_data = pyzebra.parse_1D(file, ext) pyzebra.normalize_dataset(append_data, monitor_spinner.value) pyzebra.merge_datasets(det_data, append_data) else: det_data = pyzebra.parse_1D(file, ext) pyzebra.normalize_dataset(det_data, monitor_spinner.value) pyzebra.merge_duplicates(det_data) js_data.data.update(fname=[base, base]) _init_datatable() append_upload_button.disabled = False upload_div = Div(text="or upload new .ccl/.dat files:", margin=(5, 5, 0, 5)) upload_button = FileInput(accept=".ccl,.dat", multiple=True, width=200) upload_button.on_change("value", upload_button_callback) def append_upload_button_callback(_attr, _old, new): for f_str, f_name in zip(new, append_upload_button.filename): with io.StringIO(base64.b64decode(f_str).decode()) as file: _, ext = os.path.splitext(f_name) append_data = pyzebra.parse_1D(file, ext) pyzebra.normalize_dataset(append_data, monitor_spinner.value) pyzebra.merge_datasets(det_data, append_data) _init_datatable() append_upload_div = Div(text="append extra files:", margin=(5, 5, 0, 5)) append_upload_button = FileInput(accept=".ccl,.dat", multiple=True, width=200, disabled=True) append_upload_button.on_change("value", append_upload_button_callback) def monitor_spinner_callback(_attr, old, new): if det_data: pyzebra.normalize_dataset(det_data, new) _update_plot(_get_selected_scan()) monitor_spinner = Spinner(title="Monitor:", mode="int", value=100_000, low=1, width=145) monitor_spinner.on_change("value", monitor_spinner_callback) def _update_table(): fit_ok = [(1 if "fit" in scan else 0) for scan in det_data] scan_table_source.data.update(fit=fit_ok) def _update_plot(scan): scan_motor = scan["scan_motor"] y = scan["counts"] x = scan[scan_motor] plot.axis[0].axis_label = scan_motor plot_scatter_source.data.update(x=x, y=y, y_upper=y + np.sqrt(y), y_lower=y - np.sqrt(y)) fit = scan.get("fit") if fit is not None: x_fit = np.linspace(x[0], x[-1], 100) plot_fit_source.data.update(x=x_fit, y=fit.eval(x=x_fit)) x_bkg = [] y_bkg = [] xs_peak = [] ys_peak = [] comps = fit.eval_components(x=x_fit) for i, model in enumerate(fit_params): if "linear" in model: x_bkg = x_fit y_bkg = comps[f"f{i}_"] elif any(val in model for val in ("gaussian", "voigt", "pvoigt")): xs_peak.append(x_fit) ys_peak.append(comps[f"f{i}_"]) plot_bkg_source.data.update(x=x_bkg, y=y_bkg) plot_peak_source.data.update(xs=xs_peak, ys=ys_peak) fit_output_textinput.value = fit.fit_report() else: plot_fit_source.data.update(x=[], y=[]) plot_bkg_source.data.update(x=[], y=[]) plot_peak_source.data.update(xs=[], ys=[]) fit_output_textinput.value = "" # Main plot plot = Plot( x_range=DataRange1d(), y_range=DataRange1d(only_visible=True), plot_height=470, plot_width=700, ) plot.add_layout(LinearAxis(axis_label="Counts"), place="left") plot.add_layout(LinearAxis(axis_label="Scan motor"), place="below") plot.add_layout(Grid(dimension=0, ticker=BasicTicker())) plot.add_layout(Grid(dimension=1, ticker=BasicTicker())) plot_scatter_source = ColumnDataSource( dict(x=[0], y=[0], y_upper=[0], y_lower=[0])) plot_scatter = plot.add_glyph( plot_scatter_source, Scatter(x="x", y="y", line_color="steelblue")) plot.add_layout( Whisker(source=plot_scatter_source, base="x", upper="y_upper", lower="y_lower")) plot_fit_source = ColumnDataSource(dict(x=[0], y=[0])) plot_fit = plot.add_glyph(plot_fit_source, Line(x="x", y="y")) plot_bkg_source = ColumnDataSource(dict(x=[0], y=[0])) plot_bkg = plot.add_glyph( plot_bkg_source, Line(x="x", y="y", line_color="green", line_dash="dashed")) plot_peak_source = ColumnDataSource(dict(xs=[[0]], ys=[[0]])) plot_peak = plot.add_glyph( plot_peak_source, MultiLine(xs="xs", ys="ys", line_color="red", line_dash="dashed")) fit_from_span = Span(location=None, dimension="height", line_dash="dashed") plot.add_layout(fit_from_span) fit_to_span = Span(location=None, dimension="height", line_dash="dashed") plot.add_layout(fit_to_span) plot.add_layout( Legend( items=[ ("data", [plot_scatter]), ("best fit", [plot_fit]), ("peak", [plot_peak]), ("linear", [plot_bkg]), ], location="top_left", click_policy="hide", )) plot.add_tools(PanTool(), WheelZoomTool(), ResetTool()) plot.toolbar.logo = None # Scan select def scan_table_select_callback(_attr, old, new): if not new: # skip empty selections return # Avoid selection of multiple indicies (via Shift+Click or Ctrl+Click) if len(new) > 1: # drop selection to the previous one scan_table_source.selected.indices = old return if len(old) > 1: # skip unnecessary update caused by selection drop return _update_plot(det_data[new[0]]) def scan_table_source_callback(_attr, _old, _new): _update_preview() scan_table_source = ColumnDataSource( dict(scan=[], hkl=[], fit=[], export=[])) scan_table_source.on_change("data", scan_table_source_callback) scan_table = DataTable( source=scan_table_source, columns=[ TableColumn(field="scan", title="Scan", width=50), TableColumn(field="hkl", title="hkl", width=100), TableColumn(field="fit", title="Fit", width=50), TableColumn(field="export", title="Export", editor=CheckboxEditor(), width=50), ], width=310, # +60 because of the index column height=350, autosize_mode="none", editable=True, ) scan_table_source.selected.on_change("indices", scan_table_select_callback) def _get_selected_scan(): return det_data[scan_table_source.selected.indices[0]] merge_from_select = Select(title="scan:", width=145) def merge_button_callback(): scan_into = _get_selected_scan() scan_from = det_data[int(merge_from_select.value)] if scan_into is scan_from: print("WARNING: Selected scans for merging are identical") return pyzebra.merge_scans(scan_into, scan_from) _update_plot(_get_selected_scan()) merge_button = Button(label="Merge into current", width=145) merge_button.on_click(merge_button_callback) def restore_button_callback(): pyzebra.restore_scan(_get_selected_scan()) _update_plot(_get_selected_scan()) restore_button = Button(label="Restore scan", width=145) restore_button.on_click(restore_button_callback) def fit_from_spinner_callback(_attr, _old, new): fit_from_span.location = new fit_from_spinner = Spinner(title="Fit from:", width=145) fit_from_spinner.on_change("value", fit_from_spinner_callback) def fit_to_spinner_callback(_attr, _old, new): fit_to_span.location = new fit_to_spinner = Spinner(title="to:", width=145) fit_to_spinner.on_change("value", fit_to_spinner_callback) def fitparams_add_dropdown_callback(click): # bokeh requires (str, str) for MultiSelect options new_tag = f"{click.item}-{fitparams_select.tags[0]}" fitparams_select.options.append((new_tag, click.item)) fit_params[new_tag] = fitparams_factory(click.item) fitparams_select.tags[0] += 1 fitparams_add_dropdown = Dropdown( label="Add fit function", menu=[ ("Linear", "linear"), ("Gaussian", "gaussian"), ("Voigt", "voigt"), ("Pseudo Voigt", "pvoigt"), # ("Pseudo Voigt1", "pseudovoigt1"), ], width=145, ) fitparams_add_dropdown.on_click(fitparams_add_dropdown_callback) def fitparams_select_callback(_attr, old, new): # Avoid selection of multiple indicies (via Shift+Click or Ctrl+Click) if len(new) > 1: # drop selection to the previous one fitparams_select.value = old return if len(old) > 1: # skip unnecessary update caused by selection drop return if new: fitparams_table_source.data.update(fit_params[new[0]]) else: fitparams_table_source.data.update( dict(param=[], value=[], vary=[], min=[], max=[])) fitparams_select = MultiSelect(options=[], height=120, width=145) fitparams_select.tags = [0] fitparams_select.on_change("value", fitparams_select_callback) def fitparams_remove_button_callback(): if fitparams_select.value: sel_tag = fitparams_select.value[0] del fit_params[sel_tag] for elem in fitparams_select.options: if elem[0] == sel_tag: fitparams_select.options.remove(elem) break fitparams_select.value = [] fitparams_remove_button = Button(label="Remove fit function", width=145) fitparams_remove_button.on_click(fitparams_remove_button_callback) def fitparams_factory(function): if function == "linear": params = ["slope", "intercept"] elif function == "gaussian": params = ["amplitude", "center", "sigma"] elif function == "voigt": params = ["amplitude", "center", "sigma", "gamma"] elif function == "pvoigt": params = ["amplitude", "center", "sigma", "fraction"] elif function == "pseudovoigt1": params = ["amplitude", "center", "g_sigma", "l_sigma", "fraction"] else: raise ValueError("Unknown fit function") n = len(params) fitparams = dict( param=params, value=[None] * n, vary=[True] * n, min=[None] * n, max=[None] * n, ) if function == "linear": fitparams["value"] = [0, 1] fitparams["vary"] = [False, True] fitparams["min"] = [None, 0] elif function == "gaussian": fitparams["min"] = [0, None, None] return fitparams fitparams_table_source = ColumnDataSource( dict(param=[], value=[], vary=[], min=[], max=[])) fitparams_table = DataTable( source=fitparams_table_source, columns=[ TableColumn(field="param", title="Parameter"), TableColumn(field="value", title="Value", editor=NumberEditor()), TableColumn(field="vary", title="Vary", editor=CheckboxEditor()), TableColumn(field="min", title="Min", editor=NumberEditor()), TableColumn(field="max", title="Max", editor=NumberEditor()), ], height=200, width=350, index_position=None, editable=True, auto_edit=True, ) # start with `background` and `gauss` fit functions added fitparams_add_dropdown_callback(types.SimpleNamespace(item="linear")) fitparams_add_dropdown_callback(types.SimpleNamespace(item="gaussian")) fitparams_select.value = ["gaussian-1"] # add selection to gauss fit_output_textinput = TextAreaInput(title="Fit results:", width=750, height=200) def proc_all_button_callback(): for scan, export in zip(det_data, scan_table_source.data["export"]): if export: pyzebra.fit_scan(scan, fit_params, fit_from=fit_from_spinner.value, fit_to=fit_to_spinner.value) pyzebra.get_area( scan, area_method=AREA_METHODS[area_method_radiobutton.active], lorentz=lorentz_checkbox.active, ) _update_plot(_get_selected_scan()) _update_table() proc_all_button = Button(label="Process All", button_type="primary", width=145) proc_all_button.on_click(proc_all_button_callback) def proc_button_callback(): scan = _get_selected_scan() pyzebra.fit_scan(scan, fit_params, fit_from=fit_from_spinner.value, fit_to=fit_to_spinner.value) pyzebra.get_area( scan, area_method=AREA_METHODS[area_method_radiobutton.active], lorentz=lorentz_checkbox.active, ) _update_plot(scan) _update_table() proc_button = Button(label="Process Current", width=145) proc_button.on_click(proc_button_callback) area_method_div = Div(text="Intensity:", margin=(5, 5, 0, 5)) area_method_radiobutton = RadioGroup(labels=["Function", "Area"], active=0, width=145) lorentz_checkbox = CheckboxGroup(labels=["Lorentz Correction"], width=145, margin=(13, 5, 5, 5)) export_preview_textinput = TextAreaInput(title="Export file preview:", width=500, height=400) def _update_preview(): with tempfile.TemporaryDirectory() as temp_dir: temp_file = temp_dir + "/temp" export_data = [] for s, export in zip(det_data, scan_table_source.data["export"]): if export: export_data.append(s) pyzebra.export_1D( export_data, temp_file, export_target_select.value, hkl_precision=int(hkl_precision_select.value), ) exported_content = "" file_content = [] for ext in EXPORT_TARGETS[export_target_select.value]: fname = temp_file + ext if os.path.isfile(fname): with open(fname) as f: content = f.read() exported_content += f"{ext} file:\n" + content else: content = "" file_content.append(content) js_data.data.update(content=file_content) export_preview_textinput.value = exported_content def export_target_select_callback(_attr, _old, new): js_data.data.update(ext=EXPORT_TARGETS[new]) _update_preview() export_target_select = Select(title="Export target:", options=list(EXPORT_TARGETS.keys()), value="fullprof", width=80) export_target_select.on_change("value", export_target_select_callback) js_data.data.update(ext=EXPORT_TARGETS[export_target_select.value]) def hkl_precision_select_callback(_attr, _old, _new): _update_preview() hkl_precision_select = Select(title="hkl precision:", options=["2", "3", "4"], value="2", width=80) hkl_precision_select.on_change("value", hkl_precision_select_callback) save_button = Button(label="Download File(s)", button_type="success", width=200) save_button.js_on_click( CustomJS(args={"js_data": js_data}, code=javaScript)) fitpeak_controls = row( column(fitparams_add_dropdown, fitparams_select, fitparams_remove_button), fitparams_table, Spacer(width=20), column(fit_from_spinner, lorentz_checkbox, area_method_div, area_method_radiobutton), column(fit_to_spinner, proc_button, proc_all_button), ) scan_layout = column( scan_table, row(monitor_spinner, column(Spacer(height=19), restore_button)), row(column(Spacer(height=19), merge_button), merge_from_select), ) import_layout = column( proposal_textinput, file_select, row(file_open_button, file_append_button), upload_div, upload_button, append_upload_div, append_upload_button, ) export_layout = column( export_preview_textinput, row(export_target_select, hkl_precision_select, column(Spacer(height=19), row(save_button))), ) tab_layout = column( row(import_layout, scan_layout, plot, Spacer(width=30), export_layout), row(fitpeak_controls, fit_output_textinput), ) return Panel(child=tab_layout, title="ccl integrate")
def create(palm): doc = curdoc() # Calibration averaged waveforms per photon energy waveform_plot = Plot( title=Title(text="eTOF calibration waveforms"), x_range=DataRange1d(), y_range=DataRange1d(), plot_height=760, plot_width=PLOT_CANVAS_WIDTH, toolbar_location="right", ) # ---- tools waveform_plot.toolbar.logo = None waveform_plot_hovertool = HoverTool( tooltips=[("energy, eV", "@en"), ("eTOF bin", "$x{0.}")]) waveform_plot.add_tools(PanTool(), BoxZoomTool(), WheelZoomTool(), ResetTool(), waveform_plot_hovertool) # ---- axes waveform_plot.add_layout(LinearAxis(axis_label="eTOF time bin"), place="below") waveform_plot.add_layout(LinearAxis(axis_label="Intensity", major_label_orientation="vertical"), place="left") # ---- grid lines waveform_plot.add_layout(Grid(dimension=0, ticker=BasicTicker())) waveform_plot.add_layout(Grid(dimension=1, ticker=BasicTicker())) # ---- multiline glyphs waveform_ref_source = ColumnDataSource(dict(xs=[], ys=[], en=[])) waveform_ref_multiline = waveform_plot.add_glyph( waveform_ref_source, MultiLine(xs="xs", ys="ys", line_color="blue")) waveform_str_source = ColumnDataSource(dict(xs=[], ys=[], en=[])) waveform_str_multiline = waveform_plot.add_glyph( waveform_str_source, MultiLine(xs="xs", ys="ys", line_color="red")) # ---- legend waveform_plot.add_layout( Legend(items=[( "reference", [waveform_ref_multiline]), ("streaked", [waveform_str_multiline])])) waveform_plot.legend.click_policy = "hide" # ---- vertical spans photon_peak_ref_span = Span(location=0, dimension="height", line_dash="dashed", line_color="blue") photon_peak_str_span = Span(location=0, dimension="height", line_dash="dashed", line_color="red") waveform_plot.add_layout(photon_peak_ref_span) waveform_plot.add_layout(photon_peak_str_span) # Calibration fit plot fit_plot = Plot( title=Title(text="eTOF calibration fit"), x_range=DataRange1d(), y_range=DataRange1d(), plot_height=PLOT_CANVAS_HEIGHT, plot_width=PLOT_CANVAS_WIDTH, toolbar_location="right", ) # ---- tools fit_plot.toolbar.logo = None fit_plot.add_tools(PanTool(), BoxZoomTool(), WheelZoomTool(), ResetTool()) # ---- axes fit_plot.add_layout(LinearAxis(axis_label="Photoelectron peak shift"), place="below") fit_plot.add_layout(LinearAxis(axis_label="Photon energy, eV", major_label_orientation="vertical"), place="left") # ---- grid lines fit_plot.add_layout(Grid(dimension=0, ticker=BasicTicker())) fit_plot.add_layout(Grid(dimension=1, ticker=BasicTicker())) # ---- circle glyphs fit_ref_circle_source = ColumnDataSource(dict(x=[], y=[])) fit_ref_circle = fit_plot.add_glyph( fit_ref_circle_source, Circle(x="x", y="y", line_color="blue")) fit_str_circle_source = ColumnDataSource(dict(x=[], y=[])) fit_str_circle = fit_plot.add_glyph(fit_str_circle_source, Circle(x="x", y="y", line_color="red")) # ---- line glyphs fit_ref_line_source = ColumnDataSource(dict(x=[], y=[])) fit_ref_line = fit_plot.add_glyph(fit_ref_line_source, Line(x="x", y="y", line_color="blue")) fit_str_line_source = ColumnDataSource(dict(x=[], y=[])) fit_str_line = fit_plot.add_glyph(fit_str_line_source, Line(x="x", y="y", line_color="red")) # ---- legend fit_plot.add_layout( Legend(items=[ ("reference", [fit_ref_circle, fit_ref_line]), ("streaked", [fit_str_circle, fit_str_line]), ])) fit_plot.legend.click_policy = "hide" # Calibration results datatables def datatable_ref_source_callback(_attr, _old_value, new_value): for en, ps, use in zip(new_value["energy"], new_value["peak_pos_ref"], new_value["use_in_fit"]): palm.etofs["0"].calib_data.loc[ en, "calib_tpeak"] = ps if ps != "NaN" else np.nan palm.etofs["0"].calib_data.loc[en, "use_in_fit"] = use calib_res = {} for etof_key in palm.etofs: calib_res[etof_key] = palm.etofs[etof_key].fit_calibration_curve() update_calibration_plot(calib_res) datatable_ref_source = ColumnDataSource( dict(energy=["", "", ""], peak_pos_ref=["", "", ""], use_in_fit=[True, True, True])) datatable_ref_source.on_change("data", datatable_ref_source_callback) datatable_ref = DataTable( source=datatable_ref_source, columns=[ TableColumn(field="energy", title="Photon Energy, eV", editor=IntEditor()), TableColumn(field="peak_pos_ref", title="Reference Peak", editor=IntEditor()), TableColumn(field="use_in_fit", title=" ", editor=CheckboxEditor(), width=80), ], index_position=None, editable=True, height=300, width=250, ) def datatable_str_source_callback(_attr, _old_value, new_value): for en, ps, use in zip(new_value["energy"], new_value["peak_pos_str"], new_value["use_in_fit"]): palm.etofs["1"].calib_data.loc[ en, "calib_tpeak"] = ps if ps != "NaN" else np.nan palm.etofs["1"].calib_data.loc[en, "use_in_fit"] = use calib_res = {} for etof_key in palm.etofs: calib_res[etof_key] = palm.etofs[etof_key].fit_calibration_curve() update_calibration_plot(calib_res) datatable_str_source = ColumnDataSource( dict(energy=["", "", ""], peak_pos_str=["", "", ""], use_in_fit=[True, True, True])) datatable_str_source.on_change("data", datatable_str_source_callback) datatable_str = DataTable( source=datatable_str_source, columns=[ TableColumn(field="energy", title="Photon Energy, eV", editor=IntEditor()), TableColumn(field="peak_pos_str", title="Streaked Peak", editor=IntEditor()), TableColumn(field="use_in_fit", title=" ", editor=CheckboxEditor(), width=80), ], index_position=None, editable=True, height=350, width=250, ) # eTOF calibration folder path text input def path_textinput_callback(_attr, _old_value, _new_value): path_periodic_update() update_load_dropdown_menu() path_textinput = TextInput(title="eTOF calibration path:", value=os.path.join(os.path.expanduser("~")), width=510) path_textinput.on_change("value", path_textinput_callback) # eTOF calibration eco scans dropdown def scans_dropdown_callback(_attr, _old_value, new_value): scans_dropdown.label = new_value scans_dropdown = Dropdown(label="ECO scans", button_type="default", menu=[]) scans_dropdown.on_change("value", scans_dropdown_callback) # ---- etof scans periodic update def path_periodic_update(): new_menu = [] if os.path.isdir(path_textinput.value): for entry in os.scandir(path_textinput.value): if entry.is_file() and entry.name.endswith(".json"): new_menu.append((entry.name, entry.name)) scans_dropdown.menu = sorted(new_menu, reverse=True) doc.add_periodic_callback(path_periodic_update, 5000) # Calibrate button def calibrate_button_callback(): try: palm.calibrate_etof_eco(eco_scan_filename=os.path.join( path_textinput.value, scans_dropdown.value)) except Exception: palm.calibrate_etof(folder_name=path_textinput.value) datatable_ref_source.data.update( energy=palm.etofs["0"].calib_data.index.tolist(), peak_pos_ref=palm.etofs["0"].calib_data["calib_tpeak"].tolist(), use_in_fit=palm.etofs["0"].calib_data["use_in_fit"].tolist(), ) datatable_str_source.data.update( energy=palm.etofs["0"].calib_data.index.tolist(), peak_pos_str=palm.etofs["1"].calib_data["calib_tpeak"].tolist(), use_in_fit=palm.etofs["1"].calib_data["use_in_fit"].tolist(), ) def update_calibration_plot(calib_res): etof_ref = palm.etofs["0"] etof_str = palm.etofs["1"] shift_val = 0 etof_ref_wf_shifted = [] etof_str_wf_shifted = [] for wf_ref, wf_str in zip(etof_ref.calib_data["waveform"], etof_str.calib_data["waveform"]): shift_val -= max(wf_ref.max(), wf_str.max()) etof_ref_wf_shifted.append(wf_ref + shift_val) etof_str_wf_shifted.append(wf_str + shift_val) waveform_ref_source.data.update( xs=len(etof_ref.calib_data) * [list(range(etof_ref.internal_time_bins))], ys=etof_ref_wf_shifted, en=etof_ref.calib_data.index.tolist(), ) waveform_str_source.data.update( xs=len(etof_str.calib_data) * [list(range(etof_str.internal_time_bins))], ys=etof_str_wf_shifted, en=etof_str.calib_data.index.tolist(), ) photon_peak_ref_span.location = etof_ref.calib_t0 photon_peak_str_span.location = etof_str.calib_t0 def plot_fit(time, calib_a, calib_b): time_fit = np.linspace(np.nanmin(time), np.nanmax(time), 100) en_fit = (calib_a / time_fit)**2 + calib_b return time_fit, en_fit def update_plot(calib_results, circle, line): (a, c), x, y = calib_results x_fit, y_fit = plot_fit(x, a, c) circle.data.update(x=x, y=y) line.data.update(x=x_fit, y=y_fit) update_plot(calib_res["0"], fit_ref_circle_source, fit_ref_line_source) update_plot(calib_res["1"], fit_str_circle_source, fit_str_line_source) calib_const_div.text = f""" a_str = {etof_str.calib_a:.2f}<br> b_str = {etof_str.calib_b:.2f}<br> <br> a_ref = {etof_ref.calib_a:.2f}<br> b_ref = {etof_ref.calib_b:.2f} """ calibrate_button = Button(label="Calibrate eTOF", button_type="default", width=250) calibrate_button.on_click(calibrate_button_callback) # Photon peak noise threshold value text input def phot_peak_noise_thr_spinner_callback(_attr, old_value, new_value): if new_value > 0: for etof in palm.etofs.values(): etof.photon_peak_noise_thr = new_value else: phot_peak_noise_thr_spinner.value = old_value phot_peak_noise_thr_spinner = Spinner(title="Photon peak noise threshold:", value=1, step=0.1) phot_peak_noise_thr_spinner.on_change( "value", phot_peak_noise_thr_spinner_callback) # Electron peak noise threshold value text input def el_peak_noise_thr_spinner_callback(_attr, old_value, new_value): if new_value > 0: for etof in palm.etofs.values(): etof.electron_peak_noise_thr = new_value else: el_peak_noise_thr_spinner.value = old_value el_peak_noise_thr_spinner = Spinner(title="Electron peak noise threshold:", value=10, step=0.1) el_peak_noise_thr_spinner.on_change("value", el_peak_noise_thr_spinner_callback) # Save calibration button def save_button_callback(): palm.save_etof_calib(path=path_textinput.value) update_load_dropdown_menu() save_button = Button(label="Save", button_type="default", width=250) save_button.on_click(save_button_callback) # Load calibration button def load_dropdown_callback(_attr, _old_value, new_value): if new_value: palm.load_etof_calib(os.path.join(path_textinput.value, new_value)) datatable_ref_source.data.update( energy=palm.etofs["0"].calib_data.index.tolist(), peak_pos_ref=palm.etofs["0"].calib_data["calib_tpeak"].tolist( ), use_in_fit=palm.etofs["0"].calib_data["use_in_fit"].tolist(), ) datatable_str_source.data.update( energy=palm.etofs["0"].calib_data.index.tolist(), peak_pos_str=palm.etofs["1"].calib_data["calib_tpeak"].tolist( ), use_in_fit=palm.etofs["1"].calib_data["use_in_fit"].tolist(), ) # Drop selection, so that this callback can be triggered again on the same dropdown menu # item from the user perspective load_dropdown.value = "" def update_load_dropdown_menu(): new_menu = [] calib_file_ext = ".palm_etof" if os.path.isdir(path_textinput.value): for entry in os.scandir(path_textinput.value): if entry.is_file() and entry.name.endswith((calib_file_ext)): new_menu.append( (entry.name[:-len(calib_file_ext)], entry.name)) load_dropdown.button_type = "default" load_dropdown.menu = sorted(new_menu, reverse=True) else: load_dropdown.button_type = "danger" load_dropdown.menu = new_menu doc.add_next_tick_callback(update_load_dropdown_menu) doc.add_periodic_callback(update_load_dropdown_menu, 5000) load_dropdown = Dropdown(label="Load", menu=[], width=250) load_dropdown.on_change("value", load_dropdown_callback) # eTOF fitting equation fit_eq_div = Div( text="""Fitting equation:<br><br><img src="/palm/static/5euwuy.gif">""" ) # Calibration constants calib_const_div = Div(text=f""" a_str = {0}<br> b_str = {0}<br> <br> a_ref = {0}<br> b_ref = {0} """) # assemble tab_layout = column( row( column(waveform_plot, fit_plot), Spacer(width=30), column( path_textinput, scans_dropdown, calibrate_button, phot_peak_noise_thr_spinner, el_peak_noise_thr_spinner, row(save_button, load_dropdown), row(datatable_ref, datatable_str), calib_const_div, fit_eq_div, ), )) return Panel(child=tab_layout, title="eTOF Calibration")
# initialize data source source_function1 = ColumnDataSource(data=dict(x=[], y=[])) source_function2 = ColumnDataSource(data=dict(x=[], y=[])) source_result = ColumnDataSource(data=dict(x=[], y=[])) source_convolution = ColumnDataSource(data=dict(x=[], y=[])) source_xmarker = ColumnDataSource(data=dict(x=[], y=[])) source_overlay = ColumnDataSource(data=dict(x=[], y=[], y_neg=[], y_pos=[])) # initialize properties update_is_enabled = True # initialize controls # dropdown menu for sample functions function_type = Dropdown( label="choose a sample function pair or enter one below", menu=convolution_settings.sample_function_names) function_type.on_click(function_pair_input_change) # slider controlling the evaluated x value of the convolved function x_value_input = Slider(title="x value", name='x value', value=convolution_settings.x_value_init, start=convolution_settings.x_value_min, end=convolution_settings.x_value_max, step=convolution_settings.x_value_step) x_value_input.on_change('value', input_change) # text input for the first function to be convolved function1_input = TextInput(value=convolution_settings.function1_input_init, title="my first function:") function1_input.on_change('value', input_change)
def create(): det_data = [] fit_params = {} js_data = ColumnDataSource(data=dict(content=["", ""], fname=["", ""])) def proposal_textinput_callback(_attr, _old, new): proposal = new.strip() for zebra_proposals_path in pyzebra.ZEBRA_PROPOSALS_PATHS: proposal_path = os.path.join(zebra_proposals_path, proposal) if os.path.isdir(proposal_path): # found it break else: raise ValueError(f"Can not find data for proposal '{proposal}'.") file_list = [] for file in os.listdir(proposal_path): if file.endswith((".ccl", ".dat")): file_list.append((os.path.join(proposal_path, file), file)) file_select.options = file_list file_open_button.disabled = False file_append_button.disabled = False proposal_textinput = TextInput(title="Proposal number:", width=210) proposal_textinput.on_change("value", proposal_textinput_callback) def _init_datatable(): scan_list = [s["idx"] for s in det_data] file_list = [] for scan in det_data: file_list.append(os.path.basename(scan["original_filename"])) scan_table_source.data.update( file=file_list, scan=scan_list, param=[None] * len(scan_list), fit=[0] * len(scan_list), export=[True] * len(scan_list), ) scan_table_source.selected.indices = [] scan_table_source.selected.indices = [0] scan_motor_select.options = det_data[0]["scan_motors"] scan_motor_select.value = det_data[0]["scan_motor"] param_select.value = "user defined" file_select = MultiSelect(title="Available .ccl/.dat files:", width=210, height=250) def file_open_button_callback(): nonlocal det_data det_data = [] for f_name in file_select.value: with open(f_name) as file: base, ext = os.path.splitext(f_name) if det_data: append_data = pyzebra.parse_1D(file, ext) pyzebra.normalize_dataset(append_data, monitor_spinner.value) det_data.extend(append_data) else: det_data = pyzebra.parse_1D(file, ext) pyzebra.normalize_dataset(det_data, monitor_spinner.value) js_data.data.update( fname=[base + ".comm", base + ".incomm"]) _init_datatable() append_upload_button.disabled = False file_open_button = Button(label="Open New", width=100, disabled=True) file_open_button.on_click(file_open_button_callback) def file_append_button_callback(): for f_name in file_select.value: with open(f_name) as file: _, ext = os.path.splitext(f_name) append_data = pyzebra.parse_1D(file, ext) pyzebra.normalize_dataset(append_data, monitor_spinner.value) det_data.extend(append_data) _init_datatable() file_append_button = Button(label="Append", width=100, disabled=True) file_append_button.on_click(file_append_button_callback) def upload_button_callback(_attr, _old, new): nonlocal det_data det_data = [] for f_str, f_name in zip(new, upload_button.filename): with io.StringIO(base64.b64decode(f_str).decode()) as file: base, ext = os.path.splitext(f_name) if det_data: append_data = pyzebra.parse_1D(file, ext) pyzebra.normalize_dataset(append_data, monitor_spinner.value) det_data.extend(append_data) else: det_data = pyzebra.parse_1D(file, ext) pyzebra.normalize_dataset(det_data, monitor_spinner.value) js_data.data.update( fname=[base + ".comm", base + ".incomm"]) _init_datatable() append_upload_button.disabled = False upload_div = Div(text="or upload new .ccl/.dat files:", margin=(5, 5, 0, 5)) upload_button = FileInput(accept=".ccl,.dat", multiple=True, width=200) upload_button.on_change("value", upload_button_callback) def append_upload_button_callback(_attr, _old, new): for f_str, f_name in zip(new, append_upload_button.filename): with io.StringIO(base64.b64decode(f_str).decode()) as file: _, ext = os.path.splitext(f_name) append_data = pyzebra.parse_1D(file, ext) pyzebra.normalize_dataset(append_data, monitor_spinner.value) det_data.extend(append_data) _init_datatable() append_upload_div = Div(text="append extra files:", margin=(5, 5, 0, 5)) append_upload_button = FileInput(accept=".ccl,.dat", multiple=True, width=200, disabled=True) append_upload_button.on_change("value", append_upload_button_callback) def monitor_spinner_callback(_attr, _old, new): if det_data: pyzebra.normalize_dataset(det_data, new) _update_plot() monitor_spinner = Spinner(title="Monitor:", mode="int", value=100_000, low=1, width=145) monitor_spinner.on_change("value", monitor_spinner_callback) def scan_motor_select_callback(_attr, _old, new): if det_data: for scan in det_data: scan["scan_motor"] = new _update_plot() scan_motor_select = Select(title="Scan motor:", options=[], width=145) scan_motor_select.on_change("value", scan_motor_select_callback) def _update_table(): fit_ok = [(1 if "fit" in scan else 0) for scan in det_data] scan_table_source.data.update(fit=fit_ok) def _update_plot(): _update_single_scan_plot(_get_selected_scan()) _update_overview() def _update_single_scan_plot(scan): scan_motor = scan["scan_motor"] y = scan["counts"] x = scan[scan_motor] plot.axis[0].axis_label = scan_motor plot_scatter_source.data.update(x=x, y=y, y_upper=y + np.sqrt(y), y_lower=y - np.sqrt(y)) fit = scan.get("fit") if fit is not None: x_fit = np.linspace(x[0], x[-1], 100) plot_fit_source.data.update(x=x_fit, y=fit.eval(x=x_fit)) x_bkg = [] y_bkg = [] xs_peak = [] ys_peak = [] comps = fit.eval_components(x=x_fit) for i, model in enumerate(fit_params): if "linear" in model: x_bkg = x_fit y_bkg = comps[f"f{i}_"] elif any(val in model for val in ("gaussian", "voigt", "pvoigt")): xs_peak.append(x_fit) ys_peak.append(comps[f"f{i}_"]) plot_bkg_source.data.update(x=x_bkg, y=y_bkg) plot_peak_source.data.update(xs=xs_peak, ys=ys_peak) fit_output_textinput.value = fit.fit_report() else: plot_fit_source.data.update(x=[], y=[]) plot_bkg_source.data.update(x=[], y=[]) plot_peak_source.data.update(xs=[], ys=[]) fit_output_textinput.value = "" def _update_overview(): xs = [] ys = [] param = [] x = [] y = [] par = [] for s, p in enumerate(scan_table_source.data["param"]): if p is not None: scan = det_data[s] scan_motor = scan["scan_motor"] xs.append(scan[scan_motor]) x.extend(scan[scan_motor]) ys.append(scan["counts"]) y.extend([float(p)] * len(scan[scan_motor])) param.append(float(p)) par.extend(scan["counts"]) if det_data: scan_motor = det_data[0]["scan_motor"] ov_plot.axis[0].axis_label = scan_motor ov_param_plot.axis[0].axis_label = scan_motor ov_plot_mline_source.data.update(xs=xs, ys=ys, param=param, color=color_palette(len(xs))) if y: mapper["transform"].low = np.min([np.min(y) for y in ys]) mapper["transform"].high = np.max([np.max(y) for y in ys]) ov_param_plot_scatter_source.data.update(x=x, y=y, param=par) if y: interp_f = interpolate.interp2d(x, y, par) x1, x2 = min(x), max(x) y1, y2 = min(y), max(y) image = interp_f( np.linspace(x1, x2, ov_param_plot.inner_width // 10), np.linspace(y1, y2, ov_param_plot.inner_height // 10), assume_sorted=True, ) ov_param_plot_image_source.data.update(image=[image], x=[x1], y=[y1], dw=[x2 - x1], dh=[y2 - y1]) else: ov_param_plot_image_source.data.update(image=[], x=[], y=[], dw=[], dh=[]) def _update_param_plot(): x = [] y = [] fit_param = fit_param_select.value for s, p in zip(det_data, scan_table_source.data["param"]): if "fit" in s and fit_param: x.append(p) y.append(s["fit"].values[fit_param]) param_plot_scatter_source.data.update(x=x, y=y) # Main plot plot = Plot( x_range=DataRange1d(), y_range=DataRange1d(only_visible=True), plot_height=450, plot_width=700, ) plot.add_layout(LinearAxis(axis_label="Counts"), place="left") plot.add_layout(LinearAxis(axis_label="Scan motor"), place="below") plot.add_layout(Grid(dimension=0, ticker=BasicTicker())) plot.add_layout(Grid(dimension=1, ticker=BasicTicker())) plot_scatter_source = ColumnDataSource( dict(x=[0], y=[0], y_upper=[0], y_lower=[0])) plot_scatter = plot.add_glyph( plot_scatter_source, Scatter(x="x", y="y", line_color="steelblue")) plot.add_layout( Whisker(source=plot_scatter_source, base="x", upper="y_upper", lower="y_lower")) plot_fit_source = ColumnDataSource(dict(x=[0], y=[0])) plot_fit = plot.add_glyph(plot_fit_source, Line(x="x", y="y")) plot_bkg_source = ColumnDataSource(dict(x=[0], y=[0])) plot_bkg = plot.add_glyph( plot_bkg_source, Line(x="x", y="y", line_color="green", line_dash="dashed")) plot_peak_source = ColumnDataSource(dict(xs=[[0]], ys=[[0]])) plot_peak = plot.add_glyph( plot_peak_source, MultiLine(xs="xs", ys="ys", line_color="red", line_dash="dashed")) fit_from_span = Span(location=None, dimension="height", line_dash="dashed") plot.add_layout(fit_from_span) fit_to_span = Span(location=None, dimension="height", line_dash="dashed") plot.add_layout(fit_to_span) plot.add_layout( Legend( items=[ ("data", [plot_scatter]), ("best fit", [plot_fit]), ("peak", [plot_peak]), ("linear", [plot_bkg]), ], location="top_left", click_policy="hide", )) plot.add_tools(PanTool(), WheelZoomTool(), ResetTool()) plot.toolbar.logo = None # Overview multilines plot ov_plot = Plot(x_range=DataRange1d(), y_range=DataRange1d(), plot_height=450, plot_width=700) ov_plot.add_layout(LinearAxis(axis_label="Counts"), place="left") ov_plot.add_layout(LinearAxis(axis_label="Scan motor"), place="below") ov_plot.add_layout(Grid(dimension=0, ticker=BasicTicker())) ov_plot.add_layout(Grid(dimension=1, ticker=BasicTicker())) ov_plot_mline_source = ColumnDataSource( dict(xs=[], ys=[], param=[], color=[])) ov_plot.add_glyph(ov_plot_mline_source, MultiLine(xs="xs", ys="ys", line_color="color")) hover_tool = HoverTool(tooltips=[("param", "@param")]) ov_plot.add_tools(PanTool(), WheelZoomTool(), hover_tool, ResetTool()) ov_plot.add_tools(PanTool(), WheelZoomTool(), ResetTool()) ov_plot.toolbar.logo = None # Overview perams plot ov_param_plot = Plot(x_range=DataRange1d(), y_range=DataRange1d(), plot_height=450, plot_width=700) ov_param_plot.add_layout(LinearAxis(axis_label="Param"), place="left") ov_param_plot.add_layout(LinearAxis(axis_label="Scan motor"), place="below") ov_param_plot.add_layout(Grid(dimension=0, ticker=BasicTicker())) ov_param_plot.add_layout(Grid(dimension=1, ticker=BasicTicker())) ov_param_plot_image_source = ColumnDataSource( dict(image=[], x=[], y=[], dw=[], dh=[])) ov_param_plot.add_glyph( ov_param_plot_image_source, Image(image="image", x="x", y="y", dw="dw", dh="dh")) ov_param_plot_scatter_source = ColumnDataSource(dict(x=[], y=[], param=[])) mapper = linear_cmap(field_name="param", palette=Turbo256, low=0, high=50) ov_param_plot.add_glyph( ov_param_plot_scatter_source, Scatter(x="x", y="y", line_color=mapper, fill_color=mapper, size=10), ) ov_param_plot.add_tools(PanTool(), WheelZoomTool(), ResetTool()) ov_param_plot.toolbar.logo = None # Parameter plot param_plot = Plot(x_range=DataRange1d(), y_range=DataRange1d(), plot_height=400, plot_width=700) param_plot.add_layout(LinearAxis(axis_label="Fit parameter"), place="left") param_plot.add_layout(LinearAxis(axis_label="Parameter"), place="below") param_plot.add_layout(Grid(dimension=0, ticker=BasicTicker())) param_plot.add_layout(Grid(dimension=1, ticker=BasicTicker())) param_plot_scatter_source = ColumnDataSource(dict(x=[], y=[])) param_plot.add_glyph(param_plot_scatter_source, Scatter(x="x", y="y")) param_plot.add_tools(PanTool(), WheelZoomTool(), ResetTool()) param_plot.toolbar.logo = None def fit_param_select_callback(_attr, _old, _new): _update_param_plot() fit_param_select = Select(title="Fit parameter", options=[], width=145) fit_param_select.on_change("value", fit_param_select_callback) # Plot tabs plots = Tabs(tabs=[ Panel(child=plot, title="single scan"), Panel(child=ov_plot, title="overview"), Panel(child=ov_param_plot, title="overview map"), Panel(child=column(param_plot, row(fit_param_select)), title="parameter plot"), ]) # Scan select def scan_table_select_callback(_attr, old, new): if not new: # skip empty selections return # Avoid selection of multiple indicies (via Shift+Click or Ctrl+Click) if len(new) > 1: # drop selection to the previous one scan_table_source.selected.indices = old return if len(old) > 1: # skip unnecessary update caused by selection drop return _update_plot() def scan_table_source_callback(_attr, _old, _new): _update_preview() scan_table_source = ColumnDataSource( dict(file=[], scan=[], param=[], fit=[], export=[])) scan_table_source.on_change("data", scan_table_source_callback) scan_table = DataTable( source=scan_table_source, columns=[ TableColumn(field="file", title="file", width=150), TableColumn(field="scan", title="scan", width=50), TableColumn(field="param", title="param", editor=NumberEditor(), width=50), TableColumn(field="fit", title="Fit", width=50), TableColumn(field="export", title="Export", editor=CheckboxEditor(), width=50), ], width=410, # +60 because of the index column editable=True, autosize_mode="none", ) def scan_table_source_callback(_attr, _old, _new): if scan_table_source.selected.indices: _update_plot() scan_table_source.selected.on_change("indices", scan_table_select_callback) scan_table_source.on_change("data", scan_table_source_callback) def _get_selected_scan(): return det_data[scan_table_source.selected.indices[0]] def param_select_callback(_attr, _old, new): if new == "user defined": param = [None] * len(det_data) else: param = [scan[new] for scan in det_data] scan_table_source.data["param"] = param _update_param_plot() param_select = Select( title="Parameter:", options=["user defined", "temp", "mf", "h", "k", "l"], value="user defined", width=145, ) param_select.on_change("value", param_select_callback) def fit_from_spinner_callback(_attr, _old, new): fit_from_span.location = new fit_from_spinner = Spinner(title="Fit from:", width=145) fit_from_spinner.on_change("value", fit_from_spinner_callback) def fit_to_spinner_callback(_attr, _old, new): fit_to_span.location = new fit_to_spinner = Spinner(title="to:", width=145) fit_to_spinner.on_change("value", fit_to_spinner_callback) def fitparams_add_dropdown_callback(click): # bokeh requires (str, str) for MultiSelect options new_tag = f"{click.item}-{fitparams_select.tags[0]}" fitparams_select.options.append((new_tag, click.item)) fit_params[new_tag] = fitparams_factory(click.item) fitparams_select.tags[0] += 1 fitparams_add_dropdown = Dropdown( label="Add fit function", menu=[ ("Linear", "linear"), ("Gaussian", "gaussian"), ("Voigt", "voigt"), ("Pseudo Voigt", "pvoigt"), # ("Pseudo Voigt1", "pseudovoigt1"), ], width=145, ) fitparams_add_dropdown.on_click(fitparams_add_dropdown_callback) def fitparams_select_callback(_attr, old, new): # Avoid selection of multiple indicies (via Shift+Click or Ctrl+Click) if len(new) > 1: # drop selection to the previous one fitparams_select.value = old return if len(old) > 1: # skip unnecessary update caused by selection drop return if new: fitparams_table_source.data.update(fit_params[new[0]]) else: fitparams_table_source.data.update( dict(param=[], value=[], vary=[], min=[], max=[])) fitparams_select = MultiSelect(options=[], height=120, width=145) fitparams_select.tags = [0] fitparams_select.on_change("value", fitparams_select_callback) def fitparams_remove_button_callback(): if fitparams_select.value: sel_tag = fitparams_select.value[0] del fit_params[sel_tag] for elem in fitparams_select.options: if elem[0] == sel_tag: fitparams_select.options.remove(elem) break fitparams_select.value = [] fitparams_remove_button = Button(label="Remove fit function", width=145) fitparams_remove_button.on_click(fitparams_remove_button_callback) def fitparams_factory(function): if function == "linear": params = ["slope", "intercept"] elif function == "gaussian": params = ["amplitude", "center", "sigma"] elif function == "voigt": params = ["amplitude", "center", "sigma", "gamma"] elif function == "pvoigt": params = ["amplitude", "center", "sigma", "fraction"] elif function == "pseudovoigt1": params = ["amplitude", "center", "g_sigma", "l_sigma", "fraction"] else: raise ValueError("Unknown fit function") n = len(params) fitparams = dict( param=params, value=[None] * n, vary=[True] * n, min=[None] * n, max=[None] * n, ) if function == "linear": fitparams["value"] = [0, 1] fitparams["vary"] = [False, True] fitparams["min"] = [None, 0] elif function == "gaussian": fitparams["min"] = [0, None, None] return fitparams fitparams_table_source = ColumnDataSource( dict(param=[], value=[], vary=[], min=[], max=[])) fitparams_table = DataTable( source=fitparams_table_source, columns=[ TableColumn(field="param", title="Parameter"), TableColumn(field="value", title="Value", editor=NumberEditor()), TableColumn(field="vary", title="Vary", editor=CheckboxEditor()), TableColumn(field="min", title="Min", editor=NumberEditor()), TableColumn(field="max", title="Max", editor=NumberEditor()), ], height=200, width=350, index_position=None, editable=True, auto_edit=True, ) # start with `background` and `gauss` fit functions added fitparams_add_dropdown_callback(types.SimpleNamespace(item="linear")) fitparams_add_dropdown_callback(types.SimpleNamespace(item="gaussian")) fitparams_select.value = ["gaussian-1"] # add selection to gauss fit_output_textinput = TextAreaInput(title="Fit results:", width=750, height=200) def proc_all_button_callback(): for scan, export in zip(det_data, scan_table_source.data["export"]): if export: pyzebra.fit_scan(scan, fit_params, fit_from=fit_from_spinner.value, fit_to=fit_to_spinner.value) pyzebra.get_area( scan, area_method=AREA_METHODS[area_method_radiobutton.active], lorentz=lorentz_checkbox.active, ) _update_plot() _update_table() for scan in det_data: if "fit" in scan: options = list(scan["fit"].params.keys()) fit_param_select.options = options fit_param_select.value = options[0] break _update_param_plot() proc_all_button = Button(label="Process All", button_type="primary", width=145) proc_all_button.on_click(proc_all_button_callback) def proc_button_callback(): scan = _get_selected_scan() pyzebra.fit_scan(scan, fit_params, fit_from=fit_from_spinner.value, fit_to=fit_to_spinner.value) pyzebra.get_area( scan, area_method=AREA_METHODS[area_method_radiobutton.active], lorentz=lorentz_checkbox.active, ) _update_plot() _update_table() for scan in det_data: if "fit" in scan: options = list(scan["fit"].params.keys()) fit_param_select.options = options fit_param_select.value = options[0] break _update_param_plot() proc_button = Button(label="Process Current", width=145) proc_button.on_click(proc_button_callback) area_method_div = Div(text="Intensity:", margin=(5, 5, 0, 5)) area_method_radiobutton = RadioGroup(labels=["Function", "Area"], active=0, width=145) lorentz_checkbox = CheckboxGroup(labels=["Lorentz Correction"], width=145, margin=(13, 5, 5, 5)) export_preview_textinput = TextAreaInput(title="Export file preview:", width=450, height=400) def _update_preview(): with tempfile.TemporaryDirectory() as temp_dir: temp_file = temp_dir + "/temp" export_data = [] for s, export in zip(det_data, scan_table_source.data["export"]): if export: export_data.append(s) # pyzebra.export_1D(export_data, temp_file, "fullprof") exported_content = "" file_content = [] for ext in (".comm", ".incomm"): fname = temp_file + ext if os.path.isfile(fname): with open(fname) as f: content = f.read() exported_content += f"{ext} file:\n" + content else: content = "" file_content.append(content) js_data.data.update(content=file_content) export_preview_textinput.value = exported_content save_button = Button(label="Download File(s)", button_type="success", width=220) save_button.js_on_click( CustomJS(args={"js_data": js_data}, code=javaScript)) fitpeak_controls = row( column(fitparams_add_dropdown, fitparams_select, fitparams_remove_button), fitparams_table, Spacer(width=20), column(fit_from_spinner, lorentz_checkbox, area_method_div, area_method_radiobutton), column(fit_to_spinner, proc_button, proc_all_button), ) scan_layout = column(scan_table, row(monitor_spinner, scan_motor_select, param_select)) import_layout = column( proposal_textinput, file_select, row(file_open_button, file_append_button), upload_div, upload_button, append_upload_div, append_upload_button, ) export_layout = column(export_preview_textinput, row(save_button)) tab_layout = column( row(import_layout, scan_layout, plots, Spacer(width=30), export_layout), row(fitpeak_controls, fit_output_textinput), ) return Panel(child=tab_layout, title="param study")
button = Button(label="Button (enabled) - has click event", button_type="primary") button.js_on_event("button_click", CustomJS(code="console.log('button: click ', this.toString())")) button.js_on_event("button_click", CustomJS(code="console.log('button: click ', this.toString())")) button_disabled = Button(label="Button (disabled) - no click event", button_type="primary", disabled=True) button_disabled.js_on_event("button_click", CustomJS(code="console.log('button(disabled): click ', this.toString())")) toggle_inactive = Toggle(label="Toggle button (initially inactive)", button_type="success") toggle_inactive.js_on_event('button_click', CustomJS(code="console.log('toggle(inactive): active=' + this.origin.active, this.toString())")) toggle_active = Toggle(label="Toggle button (initially active)", button_type="success", active=True) toggle_active.js_on_event('button_click', CustomJS(code="console.log('toggle(active): active=' + this.origin.active, this.toString())")) menu = [("Item 1", "item_1_value"), ("Item 2", "item_2_value"), None, ("Item 3", "item_3_value")] dropdown = Dropdown(label="Dropdown button", button_type="warning", menu=menu) dropdown.js_on_event("button_click", CustomJS(code="console.log('dropdown: click ' + this.toString())")) dropdown.js_on_event("menu_item_click", CustomJS(code="console.log('dropdown: ' + this.item, this.toString())")) dropdown_disabled = Dropdown(label="Dropdown button (disabled)", button_type="warning", disabled=True, menu=menu) dropdown_disabled.js_on_event("button_click", CustomJS(code="console.log('dropdown(disabled): click ' + this.toString())")) dropdown_disabled.js_on_event("menu_item_click", CustomJS(code="console.log('dropdown(disabled): ' + this.item, this.toString())")) dropdown_split = Dropdown(label="Split button", split=True, button_type="danger", menu=menu) dropdown_split.js_on_event("button_click", CustomJS(code="console.log('dropdown(split): click ' + this.toString())")) dropdown_split.js_on_event("menu_item_click", CustomJS(code="console.log('dropdown(split): ' + this.item, this.toString())")) checkbox_group = CheckboxGroup(labels=["Option 1", "Option 2", "Option 3"], active=[0, 1]) checkbox_group.js_on_change('active', CustomJS(code="console.log('checkbox_group: active=' + this.active, this.toString())")) radio_group = RadioGroup(labels=["Option 1", "Option 2", "Option 3"], active=0)