def slider(renderer, title, start, end, **kwargs): """ TBD """ js_callback_template = 'source.data["{key}"] = {lb}source2.data["{key}"][cb_obj.value]{rb};' lb, rb = ['[', ']'] if isinstance(renderer.glyph, bokeh.models.glyphs.Image) else ['',''] js_slider_callback_source = '\n'.join(js_callback_template.format(key=key, lb=lb, rb=rb) for key in kwargs) js_slider_callback_source += '\nsource.change.emit();' source2 = ColumnDataSource(data=kwargs) slider = Slider(start=start, end=end, value=0, step=1, title=title) slider_callback = CustomJS(args=dict(source=renderer.data_source, source2=source2), code=js_slider_callback_source) slider.js_on_change('value', slider_callback) js_button_callback_source =''' for (var i=slider.start; i <= slider.end; i++){ setTimeout((source, slider, i) => { slider.value = i; source.change.emit(); }, 20 * i, source, slider, i); } ''' button_callback = CustomJS(args=dict(slider=slider, source=renderer.data_source, source2=source2), code=js_button_callback_source) button = Button(label="Play", button_type="success") button.js_on_event(events.ButtonClick, button_callback) return row(slider, button)
def build_button(self, button_size_pixels: int) -> Button: js_code_for_adio_playing = self._generate_js_code_for_audio_playing() callback = CustomJS(code=js_code_for_adio_playing) audio_button = Button(label='', css_classes=[self.button_id], width=button_size_pixels, height=button_size_pixels) audio_button.js_on_event(ButtonClick, callback) return audio_button
def run(): print("run") dx = np.gradient(x) dy = np.gradient(y) ## source.data = dict(x=x, y=dy) t1_stop = Event() thread = Thread(target=threaded_function, args=(1, t1_stop)) thread.start() thread.join() print("Process finished...exiting") # Set up layouts and add to document inputs = widgetbox(dropdown, text, offset, amplitude, Speed, Delay, button, button1) button.js_on_event(events.ButtonClick, display_event(div)) dropdown.on_change('value', function_to_call) button.on_click(run) button1.on_click(stop) curdoc().add_root(row(inputs, plot, width=1200)) curdoc().title = "Sliders" def threaded_function(arg, stop_event): while (not stop_event.is_set()): for i in range(len(x)): print(x[i]) circle = Circle(x=x[i], y=y[i], fill_color='red', size=3) ## initial_circle = Circle(x='x', y='y', fill_color='blue', size=50)
# Further options to implement ("Export SVG","expo_svg"), ("Export high-res PNG","expo_svg")] ser_act.js_on_change('value', ser_callback) posneg_menu=[('Positive Mode','Positive'), ('Negative Mode', 'Negative')] posneg = Dropdown(menu=posneg_menu, value='Positive', label='Instrument Mode: +' , width=160, height=30) graph_opt = Toggle(label='Figure', width=110, height=30) help_alert = CustomJS(args=dict(), code=open(os.path.join(os.getcwd(), 'JS_Functions', "help_cb.js")).read()) mass_finder = Toggle(label='Mass Finder', width=110, height=30) mass_match = Toggle(label='Complex Stoichiometry', width=150, height=30) dt_button = Toggle(label='Data Processing', width=110, height=30) help_button = Button(label='Help', width=90, height=30) help_button.js_on_event(ButtonClick, help_alert) sele_cb = CustomJS(args=dict(peak_mz=peak_mz, bg_mz=bg_mz, proc_mz=proc_mz, ser_act=ser_act, series_sele=series_sele, series_data=series_data, series_mz=series_mz, series_names=series_names, series_masses=series_masses, pp_mean_data=pp_mean_data, pp_std_data=pp_std_data, aser_data=aser_data, posneg=posneg, series_colours_DS=series_colours_DS), code=open(os.path.join(os.getcwd(), 'JS_Functions', "navia_functions.js")).read() + open(os.path.join(os.getcwd(), 'JS_Functions', "sele_cb.js")).read()) correct_cb = CustomJS(args=dict(series_colours_DS=series_colours_DS, peak_mz=peak_mz, bg_mz=bg_mz, proc_mz=proc_mz, ser_act=ser_act, series_sele=series_sele, series_data=series_data, series_mz=series_mz, series_names=series_names, series_masses=series_masses, pp_mean_data=pp_mean_data, pp_std_data=pp_std_data, aser_data=aser_data, posneg=posneg), code=open(os.path.join(os.getcwd(), 'JS_Functions', "navia_functions.js")).read() + open(os.path.join(os.getcwd(), 'JS_Functions', "correct_cb.js")).read()) posneg_cb = CustomJS(args=dict(series_colours_DS=series_colours_DS, peak_mz=peak_mz, bg_mz=bg_mz, proc_mz=proc_mz, ser_act=ser_act, series_sele=series_sele, series_data=series_data, series_mz=series_mz, series_names=series_names, series_masses=series_masses, pp_mean_data=pp_mean_data, pp_std_data=pp_std_data, aser_data=aser_data, posneg=posneg), code=open(os.path.join(os.getcwd(), 'JS_Functions', "navia_functions.js")).read() + open(os.path.join(os.getcwd(), 'JS_Functions', "posneg_cb.js")).read()) posneg.js_on_change('value', posneg_cb) class DQTapTool(TapTool):
value=str(DEFAULT_DETECTOR_THICKNESS)) detector_density_input = TextInput(title='density', value=str(DEFAULT_DETECTOR_DENSITY)) p = Paragraph(text="", width=500) p.text = f"Running roentgen version {roentgen.__version__}" columns = [ TableColumn(field="x", title="energy [keV]"), TableColumn(field="y", title="Percent"), ] data_table = DataTable(source=source, columns=columns, width=400, height=700) download_button = Button(label="Download", button_type="success") download_button.js_on_event( ButtonClick, CustomJS(args=dict(source=source), code=open(join(dirname(__file__), "download.js")).read())) def convert_air_pressure(value, current_unit, new_unit): if current_unit == "atm": air_pressure = u.Quantity(value * const.atm, "Pa") elif current_unit == "torr": air_pressure = u.Quantity(value * const.atm / 760., "Pa") else: air_pressure = u.Quantity(value, current_unit) if new_unit == "atm": return (air_pressure.to("Pa") / const.atm).value elif new_unit == "torr": return (air_pressure.to("Pa") / const.atm).value * 760.0
def intplotter(data, isodata, nodata, y, hetclassintdf): linewidth = 1.5 source = ColumnDataSource(data) s2 = ColumnDataSource(data=dict(mz=data["mz"], Error=data["Error"], RA=data["RA"], Formula=data["Formula"], HeteroClass=data["HeteroClass"])) isosource = ColumnDataSource(isodata) nosource = ColumnDataSource(nodata) url = "http://www.chemspider.com/Search.aspx?q=@Formula" TOOLS = "crosshair,pan,wheel_zoom,box_zoom,reset,tap,save,box_select,poly_select,lasso_select,hover" figdims = (900, 500) #pixel dimensions for the normal figures msxlim = [200, 700] #x limits in m/z for the mass spectra vkxlim = [0, 1] vkylim = [0, 2] p1 = figure(tools=TOOLS, title=y[:-9] + " - Van Krevelen", width=figdims[0], height=figdims[1], x_axis_label='O/C', y_axis_label='H/C', x_range=vkxlim, y_range=vkylim) color_mapper = LinearColorMapper(palette=glocmap, low=msxlim[0], high=msxlim[1]) p1.scatter(x='OC', y='HC', source=source, size='VKsize', fill_color={ 'field': 'mz', 'transform': color_mapper }, fill_alpha=0.75, line_color=None) #use size not radius. hover = p1.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([('Formula', "@Formula"), ('Mass', "@mz{1.11111}"), ('Error (ppm)', "@Error{1.11}")]) taptool = p1.select(type=TapTool) taptool.callback = OpenURL(url=url) color_bar = ColorBar(color_mapper=color_mapper, title="m/z", border_line_color=None, location=(0, 0), scale_alpha=0.7) #orientation='horizontal',location='top_left', scale_alpha=0.7)#,ticker=FixedTicker(ticks=[2,6,10,14,18])) p1.add_layout(color_bar, "right") dbexlim = [0, 45] dbeylim = [0, 40] cmax = max(data["O"]) cmax = int(5 * round(float(cmax) / 5)) p2 = figure(tools=TOOLS, title=y[:-9] + " - DBE vs C# Plot", width=figdims[0], height=figdims[1], x_axis_label='C#', y_axis_label='DBE', x_range=dbexlim, y_range=dbeylim) color_mapper2 = LinearColorMapper(palette=glocmap2, low=0, high=cmax) p2.scatter(x='C', y='DBE', source=source, size='VKsize', fill_color={ 'field': 'O', 'transform': color_mapper2 }, fill_alpha=0.75, line_color=None) hover = p2.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([('Formula', "@Formula"), ('Mass', "@mz{1.11111}"), ('Error (ppm)', "@Error{1.11}")]) taptool = p2.select(type=TapTool) taptool.callback = OpenURL(url=url) color_bar2 = ColorBar( color_mapper=color_mapper2, title="O#", border_line_color=None, location=(0, 0), scale_alpha=0.7, ticker=FixedTicker( ticks=[0, int(cmax / 4), int(cmax / 2), int(3 * cmax / 4), cmax])) p2.add_layout(color_bar2, "right") aixlim = [0, 45] aiylim = [0, 1] p3 = figure(tools=TOOLS, title=y[:-9] + " - AI(mod) vs C# Plot", width=figdims[0], height=figdims[1], x_axis_label='C#', y_axis_label='AI(mod)', x_range=aixlim, y_range=aiylim) color_mapper3 = LinearColorMapper(palette=glocmap2, low=0, high=cmax) p3.scatter(x='C', y='AImod', source=source, size='VKsize', fill_color={ 'field': 'O', 'transform': color_mapper3 }, fill_alpha=0.75, line_color=None) hover = p3.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([('Formula', "@Formula"), ('Mass', "@mz{1.11111}"), ('Error (ppm)', "@Error{1.11}")]) taptool = p3.select(type=TapTool) taptool.callback = OpenURL(url=url) color_bar3 = ColorBar( color_mapper=color_mapper3, title="O#", border_line_color=None, location=(0, 0), scale_alpha=0.7, ticker=FixedTicker( ticks=[0, int(cmax / 4), int(cmax / 2), int(3 * cmax / 4), cmax])) #orientation='horizontal',location='top_left', scale_alpha=0.7)#,ticker=FixedTicker(ticks=[2,6,10,14,18])) p3.add_layout(color_bar3, "right") p4 = figure(tools=TOOLS, title=y[:-9] + " - Assigned Centroid MS", width=figdims[0], height=figdims[1], x_axis_label='m/z', y_axis_label='Abundance', y_range=[min(data["RA"]), max(data["RA"])], x_range=msxlim) p4.segment(x0=0, x1=800, y0=0, y1=0, line_width=1, line_color="black") p4.segment(x0='mz', y0=0, x1='mz', y1='RA', source=source, line_width=linewidth, line_color="black") p4.scatter(x='mz', y='RA', source=source, fill_color='black', line_color=None) hover = p4.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([('Formula', "@Formula"), ('Mass', "@mz{1.11111}"), ('Error (ppm)', "@Error{1.11}")]) taptool = p4.select(type=TapTool) taptool.callback = OpenURL(url=url) p4.yaxis[0].formatter = PrintfTickFormatter(format="%4.1e") """ #this is me trying to plot a barplot of heteroatomic class distributions... p7 = figure(tools=TOOLS, title=y[:-9]+"",width=800, height=600, x_axis_label='HeteroClass',y_axis_label='Count',webgl=True) p7.quad(left="HetClassInts",y=hetclassdf[0],source=source,width=5,height=) t7 = layouts.Column(hist) tab7 = Panel(child=t7,title="test") """ stretch = msxlim[0] * 0.1 p5 = figure(tools=TOOLS, title=y[:-9] + " - Assigned Centroid MS", width=1400, height=600, x_axis_label='m/z', y_axis_label='Abundance', y_range=[min(data["RA"]), max(data["RA"])], x_range=(msxlim[0] - stretch, msxlim[1] + stretch)) p5.segment(x0=0, x1=800, y0=0, y1=0, line_width=1, line_color="black") no1 = p5.segment(x0='mz', y0=0, x1='mz', y1='RA', source=nosource, line_width=linewidth, line_color="red", legend_label="Unassigned Peaks") #no2 =p5.scatter(x='mz', y='RA',source=nosource,fill_color='red',line_color=None,legend_label="Unassigned Peaks") #p5.scatter(x='mz', y='RA',source=source,fill_color='black',line_color=None,legend_label="Assigned Peaks") p5.segment(x0='mz', y0=0, x1='mz', y1='RA', source=source, line_width=linewidth, line_color="black", legend_label="Assigned Peaks") iso1 = p5.segment(x0='mz', y0=0, x1='mz', y1='RA', source=isosource, line_width=linewidth, line_color="green", legend_label="Isotologue Peaks") #iso2 =p5.scatter(x='mz', y='RA',source=isosource,fill_color='green',line_color=None, legend_label="Isotologue Peaks") hover = p5.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([('Formula', "@Formula"), ('Mass', "@mz{1.11111}"), ('Error (ppm)', "@Error{1.11}")]) taptool = p5.select(type=TapTool) taptool.callback = OpenURL(url=url) p5.yaxis[0].formatter = PrintfTickFormatter(format="%4.1e") #Bokeh 2 doesnt need JS buttons thanks to interactive legends #js_code1 = "iso1.glyph.visible = false; iso2.glyph.visible = false; no1.glyph.visible = false; no2.glyph.visible = false;" #cb1 = CustomJS(code=js_code1, args=dict(iso1=iso1,iso2=iso2,no1=no1,no2=no2)) #js_code2 = "iso1.glyph.visible = true; iso2.glyph.visible = true; no1.glyph.visible = true; no2.glyph.visible = true;" #cb2 = CustomJS(code=js_code2, args=dict(iso1=iso1,iso2=iso2,no1=no1,no2=no2)) #toggleOn = Button(label="Hide", button_type="success") #toggleOn.js_on_click(cb1) #toggleOff = Button(label="Show", button_type="success") #toggleOn.js_on_click(cb2) #top = layouts.Row(toggleOn,toggleOff) t3 = layouts.Column(p5) p5.legend.location = "top_left" p5.legend.click_policy = "hide" tab3 = Panel(child=t3, title="Centroid MS with Isotopomers and No Hits") downloadbutton = Button(label="Download", button_type="success") cb3 = CustomJS(args=dict(s2=s2), code=""" var data = s2.get('data'); var filetext = 'mz,Error,RA,Formula,HeteroClass\\n'; for (i=0; i < data['mz'].length; i++) { var currRow = [data['mz'][i].toString(), data['Error'][i].toString(), data['RA'][i].toString(), data['Formula'][i].toString(), data['HeteroClass'][i].toString().concat('\\n')]; var joined = currRow.join(); filetext = filetext.concat(joined); } var filename = 'data_result.csv'; var blob = new Blob([filetext], { type: 'text/csv;charset=utf-8;' }); //addresses IE if (navigator.msSaveBlob) { navigator.msSaveBlob(blob, filename); } else { var link = document.createElement("a"); link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = filename; link.target = "_blank"; link.style.visibility = 'hidden'; link.dispatchEvent(new MouseEvent('click')) } """) downloadbutton.js_on_event(ButtonClick, cb3) columns = [ TableColumn(field="mz", title="m/z", formatter=NumberFormatter(format="0.00000")), TableColumn(field="Error", title="Error (ppm)", formatter=NumberFormatter(format="0.00")), TableColumn(field="RA", title="Abundance"), TableColumn(field="Formula", title="Formula"), TableColumn(field="HeteroClass", title="Heteroatomic Class") ] data_table = DataTable(source=s2, columns=columns, width=1400, header_row=False, fit_columns=True) t4 = layouts.Column(data_table, downloadbutton) tab4 = Panel(child=t4, title="Selected Data Table") cb4 = CustomJS(args=dict(s2=s2, dt=data_table), code=""" var inds = cb_obj.get('selected')['1d'].indices; var d1 = cb_obj.get('data'); var d2 = s2.get('data'); if (inds.length == 0) { d2['mz'] = d1['mz'] d2['Error'] = d1['Error'] d2['RA'] = d1['RA'] d2['Formula'] = d1['Formula'] d2['HeteroClass'] = d1['HeteroClass'] } else if (inds.length != 0) { d2['mz'] = [] d2['Error'] = [] d2['RA'] = [] d2['Formula'] = [] d2['HeteroClass'] = [] for (i = 0; i < inds.length; i++) { d2['mz'].push(d1['mz'][inds[i]]) d2['Error'].push(d1['Error'][inds[i]]) d2['RA'].push(d1['RA'][inds[i]]) d2['Formula'].push(d1['Formula'][inds[i]]) d2['HeteroClass'].push(d1['HeteroClass'][inds[i]]) } } s2.trigger('change'); dt.trigger('change'); """) source.selected.js_on_change('indices', cb4) """ hetclasslist = hetclassintdf["HetClass"].tolist() hetclasslistnew = [] for x in hetclasslist: hetclasslistnew.append([x,x]) dropdown = Dropdown(label="Dropdown button", button_type="warning", menu=hetclasslistnew) """ t1 = layouts.Row(p1, p4) t2 = layouts.Row(p2, p3) t12 = layouts.Column(t1, t2) tab1 = Panel(child=t12, title="Main") tabs = Tabs(tabs=[tab1, tab3, tab4]) for figs in [p1, p2, p3, p4, p5]: figs.xaxis.axis_label_text_font_size = "14pt" figs.xaxis.major_label_text_font_size = "14pt" figs.yaxis.axis_label_text_font_size = "14pt" figs.yaxis.major_label_text_font_size = "14pt" figs.title.text_font_size = "14pt" figs.toolbar_location = "above" with open(outputpath + 'templates/index.html', 'r') as f: template = Template(f.read()) #js_resources = JSResources(mode='inline') html = file_html(tabs, (CDN, CDN), "Interactive Van Krevelen Diagrams", template=template) output_file2 = outputpath + y[:-9] + '-plot.html' with open(output_file2, 'w') as f: f.write(html) view(output_file2)
# """) # bu = template.format(stt_button = stt_button) # div = Div(text=bu, height=300) stt_button.js_on_event( "button_click", CustomJS(code=""" var recognition = new webkitSpeechRecognition(); recognition.continuous = true; recognition.interimResults = true; recognition.onresult = function (e) { var value = ""; for (var i = e.resultIndex; i < e.results.length; ++i) { if (e.results[i].isFinal) { value += e.results[i][0].transcript; } } if ( value != "") { document.dispatchEvent(new CustomEvent("GET_TEXT", {detail: value})); } } recognition.start(); this.addclass='active' """)) result = streamlit_bokeh_events(stt_button, events="GET_TEXT", key="listen", refresh_on_update=False,
# # Save Data Button save_data_button = Button(label=raman_languages.TEXT__SAVE_CURRENT_DATA) save_data_button.on_click(callback_save_data_button) # # 20180704: just another way of saving data with open(os.path.join(__location__, 'static/js/export_to_json.js'), 'r') as f: export_to_json_js = f.read() export_data_as_json_button_callback = CustomJS(args=dict( source=waveform_data_source, ), code=export_to_json_js) export_data_as_json_button = Button( label=raman_languages.TEXT__EXPORT_CURRENT_DATA_AS_JSON) export_data_as_json_button.js_on_event('tap', export_data_as_json_button_callback) with open(os.path.join(__location__, 'static/js/export_to_csv.js'), 'r') as f: export_to_csv_js = f.read() export_data_as_csv_button_callback = CustomJS(args=dict( source=waveform_data_source, ), code=export_to_csv_js) export_data_as_csv_button = Button( label=raman_languages.TEXT__EXPORT_CURRENT_DATA_AS_CSV) export_data_as_csv_button.js_on_event('tap', export_data_as_csv_button_callback) # Load file button with open(os.path.join(__location__, 'static/js/load_file.js'), 'r') as f:
import streamlit as st from bokeh.models.widgets import Button from bokeh.models import CustomJS from streamlit_bokeh_events import streamlit_bokeh_events loc_button = Button(label="Get Location") loc_button.js_on_event( "button_click", CustomJS(code=""" navigator.geolocation.getCurrentPosition( (loc) => { document.dispatchEvent(new CustomEvent("GET_LOCATION", {detail: {lat: loc.coords.latitude, lon: loc.coords.longitude}})) } ) """)) result = streamlit_bokeh_events(loc_button, events="GET_LOCATION", key="get_location", refresh_on_update=False, override_height=75, debounce_time=0) if result: if "GET_LOCATION" in result: st.write(result.get("GET_LOCATION"))
import streamlit as st from bokeh.models.widgets import Button from bokeh.models import CustomJS from streamlit_bokeh_events import streamlit_bokeh_events from io import StringIO import pandas as pd copy_button = Button(label="Get Clipboard Data") copy_button.js_on_event( "button_click", CustomJS(code=""" navigator.clipboard.readText().then(text => document.dispatchEvent(new CustomEvent("GET_TEXT", {detail: text}))) """)) result = streamlit_bokeh_events(copy_button, events="GET_TEXT", key="get_text", refresh_on_update=False, override_height=75, debounce_time=0) if result: if "GET_TEXT" in result: df = pd.read_csv(StringIO(result.get("GET_TEXT"))) st.table(df)
def make_document(doc): print(doc) data = ColumnDataSource(dict( time=[], acc_x=[], acc_y=[], acc_z=[], accg_x=[], accg_y=[], accg_z=[], rot_a=[], rot_b=[], rot_g=[], rotr_a=[], rotr_b=[], rotr_g=[], # angle_dev=[] )) p = figure(x_axis_type="datetime", tools='pan,wheel_zoom,box_zoom,reset,save', plot_width=1100) p.line(x='time', y='acc_x', line_color="red", line_width=1, source=data) p.line(x='time', y='acc_y', line_color="green", line_width=1, source=data) p.line(x='time', y='acc_z', line_color="blue", line_width=1, source=data) p.line(x='time', y='rot_a', line_color="cyan", line_width=1, source=data) p.line(x='time', y='rot_b', line_color="black", line_width=1, source=data) p.line(x='time', y='rot_g', line_color="orange", line_width=1, source=data) mid_box = BoxAnnotation(left=2, right=6, fill_alpha=0.1, fill_color='blue') vline = Span(location=0, dimension='height', line_color='red', line_width=3) p.renderers.extend([vline]) # p.add_layout(low_box) p.add_layout(mid_box) # p.add_layout(high_box) p.title.text = "Sitting Project Labeling Tool v0.1" p.xgrid[0].grid_line_color = None p.ygrid[0].grid_line_alpha = 0.5 p.xaxis.axis_label = 'Time' p.yaxis.axis_label = 'Value' div = Div(width=400, height=p.plot_height, height_policy="fixed") ## Events with no attributes # p.js_on_event(events.LODStart, display_event(div)) # Start of LOD display # p.js_on_event(events.LODEnd, display_event(div)) # End of LOD display ## Events with attributes # point_attributes = ['x', 'y', 'sx', 'sy'] # Point events # wheel_attributes = point_attributes + ['delta'] # Mouse wheel event # pan_attributes = point_attributes + ['delta_x', 'delta_y'] # Pan event # pinch_attributes = point_attributes + ['scale'] # Pinch event point_events = [ events.Tap, # events.DoubleTap, events.Press, # events.MouseMove, events.MouseEnter, events.MouseLeave, # events.PinchStart, events.PinchEnd, # events.PanStart, events.PanEnd, ] # for event in point_events: # p.js_on_event(event, display_event(div, attributes=point_attributes)) p.js_on_event(events.Tap, CustomJS(args=dict(vline=vline), code=""" vline.location = cb_obj['x']; """)) btn_start = Button(label="Mark as START", button_type="success") btn_end = Button(label="Mark as END", button_type="success") btn_start.js_on_event(events.ButtonClick, CustomJS(args=dict(div=div, mid_box=mid_box, vline=vline), code=""" mid_box.left = vline.location; mid_box.right = null; """) ) def write_csv(): print(mid_box.left, mid_box.right) btn_sit1 = Button(label="cross-leg") btn_sit2 = Button(label="lean-right") btn_sit3 = Button(label="lean-back") # btn_sit4 = Button(label="sit4") btn_sit1.js_on_event(events.ButtonClick, CustomJS( args=dict(div=div, mid_box=mid_box, vline=vline), code=""" mid_box.right = vline.location; console.log(mid_box.left, mid_box.right, "cross-leg"); """) ) btn_sit2.js_on_event(events.ButtonClick, CustomJS( args=dict(div=div, mid_box=mid_box, vline=vline), code=""" mid_box.right = vline.location; console.log(mid_box.left, mid_box.right, "lean-right"); """) ) btn_sit3.js_on_event(events.ButtonClick, CustomJS( args=dict(div=div, mid_box=mid_box, vline=vline), code=""" mid_box.right = vline.location; console.log(mid_box.left, mid_box.right, "lean-back"); """) ) # btn_sit4.js_on_event(events.ButtonClick, # CustomJS( # args=dict(div=div, mid_box=mid_box, vline=vline), # code=""" # mid_box.right = vline.location; # console.log(mid_box.left, mid_box.right, "sit4"); # """) # ) btn_write= Button(label="Write CSV") btn_write.on_click(lambda : write_csv()) # p.js_on_event(events.MouseWheel, display_event(div, attributes=wheel_attributes)) # p.js_on_event(events.Pan, display_event(div, attributes=pan_attributes)) # p.js_on_event(events.Pinch, display_event(div, attributes=pinch_attributes)) doc.add_periodic_callback(lambda: update_from_sensor(data), 60) doc.add_root( column( row(p, div), row(btn_start), row(column( btn_sit1, btn_sit2, btn_sit3, ) ) ) )
class ViewerVIWidgets(object): """ Encapsulates Bokeh widgets, and related callbacks, used for VI """ def __init__(self, title, viewer_cds): self.vi_quality_labels = [ x["label"] for x in vi_flags if x["type"]=="quality" ] self.vi_issue_labels = [ x["label"] for x in vi_flags if x["type"]=="issue" ] self.vi_issue_slabels = [ x["shortlabel"] for x in vi_flags if x["type"]=="issue" ] self.js_files = get_resources('js') self.title = title self.vi_countdown_toggle = None #- List of fields to be recorded in output csv file, contains for each field: # [ field name (in VI file header), associated variable in viewer_cds.cds_metadata ] self.output_file_fields = [] for file_field in vi_file_fields: if file_field[1] in viewer_cds.cds_metadata.data.keys() : self.output_file_fields.append([file_field[0], file_field[1]]) def add_filename(self, username='******'): #- VI file name default_vi_filename = "desi-vi_"+self.title default_vi_filename += ("_"+username) default_vi_filename += ".csv" self.vi_filename_input = TextInput(value=default_vi_filename, title="VI file name:") def add_vi_issues(self, viewer_cds, widgets): #- Optional VI flags (issues) self.vi_issue_input = CheckboxGroup(labels=self.vi_issue_labels, active=[]) vi_issue_code = self.js_files["CSVtoArray.js"] + self.js_files["save_vi.js"] vi_issue_code += """ var issues = [] for (var i=0; i<vi_issue_labels.length; i++) { if (vi_issue_input.active.indexOf(i) >= 0) issues.push(vi_issue_slabels[i]) } if (issues.length > 0) { cds_metadata.data['VI_issue_flag'][ifiberslider.value] = ( issues.join('') ) } else { cds_metadata.data['VI_issue_flag'][ifiberslider.value] = " " } autosave_vi_localStorage(output_file_fields, cds_metadata.data, title) cds_metadata.change.emit() """ self.vi_issue_callback = CustomJS( args=dict(cds_metadata = viewer_cds.cds_metadata, ifiberslider = widgets.ifiberslider, vi_issue_input = self.vi_issue_input, vi_issue_labels = self.vi_issue_labels, vi_issue_slabels = self.vi_issue_slabels, title = self.title, output_file_fields = self.output_file_fields), code = vi_issue_code ) self.vi_issue_input.js_on_click(self.vi_issue_callback) def add_vi_z(self, viewer_cds, widgets): ## TODO: z_tovi behaviour if with_vi_widget=False ..? #- Optional VI information on redshift self.vi_z_input = TextInput(value='', title="VI redshift:") vi_z_code = self.js_files["CSVtoArray.js"] + self.js_files["save_vi.js"] vi_z_code += """ cds_metadata.data['VI_z'][ifiberslider.value]=vi_z_input.value autosave_vi_localStorage(output_file_fields, cds_metadata.data, title) cds_metadata.change.emit() """ self.vi_z_callback = CustomJS( args=dict(cds_metadata = viewer_cds.cds_metadata, ifiberslider = widgets.ifiberslider, vi_z_input = self.vi_z_input, title = self.title, output_file_fields=self.output_file_fields), code = vi_z_code ) self.vi_z_input.js_on_change('value', self.vi_z_callback) # Copy z value from redshift slider to VI self.z_tovi_button = Button(label='Copy z to VI') self.z_tovi_callback = CustomJS( args=dict(z_input=widgets.z_input, vi_z_input=self.vi_z_input), code=""" vi_z_input.value = z_input.value """) self.z_tovi_button.js_on_event('button_click', self.z_tovi_callback) def add_vi_spectype(self, viewer_cds, widgets): #- Optional VI information on spectral type self.vi_category_select = Select(value=' ', title="VI spectype:", options=([' '] + vi_spectypes)) # The default value set to ' ' as setting value='' does not seem to work well with Select. vi_category_code = self.js_files["CSVtoArray.js"] + self.js_files["save_vi.js"] vi_category_code += """ if (vi_category_select.value == ' ') { cds_metadata.data['VI_spectype'][ifiberslider.value]='' } else { cds_metadata.data['VI_spectype'][ifiberslider.value]=vi_category_select.value } autosave_vi_localStorage(output_file_fields, cds_metadata.data, title) cds_metadata.change.emit() """ self.vi_category_callback = CustomJS( args=dict(cds_metadata=viewer_cds.cds_metadata, ifiberslider = widgets.ifiberslider, vi_category_select=self.vi_category_select, title=self.title, output_file_fields=self.output_file_fields), code=vi_category_code ) self.vi_category_select.js_on_change('value', self.vi_category_callback) def add_vi_comment(self, viewer_cds, widgets): #- Optional VI comment self.vi_comment_input = TextInput(value='', title="VI comment (see guidelines):") vi_comment_code = self.js_files["CSVtoArray.js"] + self.js_files["save_vi.js"] vi_comment_code += """ var stored_comment = (vi_comment_input.value).replace(/./g, function(char){ if ( char==',' ) { return ';' } else if ( char.charCodeAt(0)<=127 ) { return char } else { var char_list = ['Å','α','β','γ','δ','λ'] var char_replace = ['Angstrom','alpha','beta','gamma','delta','lambda'] for (var i=0; i<char_list.length; i++) { if ( char==char_list[i] ) return char_replace[i] } return '?' } }) cds_metadata.data['VI_comment'][ifiberslider.value] = stored_comment autosave_vi_localStorage(output_file_fields, cds_metadata.data, title) cds_metadata.change.emit() """ self.vi_comment_callback = CustomJS( args=dict(cds_metadata = viewer_cds.cds_metadata, ifiberslider = widgets.ifiberslider, vi_comment_input = self.vi_comment_input, title=self.title, output_file_fields=self.output_file_fields), code=vi_comment_code ) self.vi_comment_input.js_on_change('value',self.vi_comment_callback) #- List of "standard" VI comment self.vi_std_comment_select = Select(value=" ", title="Standard comment:", options=([' '] + vi_std_comments)) vi_std_comment_code = """ if (vi_std_comment_select.value != ' ') { if (vi_comment_input.value != '') { vi_comment_input.value = vi_comment_input.value + " " + vi_std_comment_select.value } else { vi_comment_input.value = vi_std_comment_select.value } } """ self.vi_std_comment_callback = CustomJS( args = dict(vi_std_comment_select = self.vi_std_comment_select, vi_comment_input = self.vi_comment_input), code = vi_std_comment_code ) self.vi_std_comment_select.js_on_change('value', self.vi_std_comment_callback) def add_vi_quality(self, viewer_cds, widgets): #- Main VI quality widget self.vi_quality_input = RadioButtonGroup(labels=self.vi_quality_labels) vi_quality_code = self.js_files["CSVtoArray.js"] + self.js_files["save_vi.js"] vi_quality_code += """ if ( vi_quality_input.active >= 0 ) { cds_metadata.data['VI_quality_flag'][ifiberslider.value] = vi_quality_labels[vi_quality_input.active] } else { cds_metadata.data['VI_quality_flag'][ifiberslider.value] = "-1" } autosave_vi_localStorage(output_file_fields, cds_metadata.data, title) cds_metadata.change.emit() """ self.vi_quality_callback = CustomJS( args = dict(cds_metadata = viewer_cds.cds_metadata, vi_quality_input = self.vi_quality_input, vi_quality_labels = self.vi_quality_labels, ifiberslider = widgets.ifiberslider, title=self.title, output_file_fields = self.output_file_fields), code=vi_quality_code ) self.vi_quality_input.js_on_click(self.vi_quality_callback) def add_vi_scanner(self, viewer_cds): #- VI scanner name self.vi_name_input = TextInput(value=(viewer_cds.cds_metadata.data['VI_scanner'][0]).strip(), title="Your name (3-letter acronym):") vi_name_code = self.js_files["CSVtoArray.js"] + self.js_files["save_vi.js"] vi_name_code += """ for (var i=0; i<(cds_metadata.data['VI_scanner']).length; i++) { cds_metadata.data['VI_scanner'][i]=vi_name_input.value } var newname = vi_filename_input.value var name_chunks = newname.split("_") newname = ( name_chunks.slice(0,name_chunks.length-1).join("_") ) + ("_"+vi_name_input.value+".csv") vi_filename_input.value = newname autosave_vi_localStorage(output_file_fields, cds_metadata.data, title) """ self.vi_name_callback = CustomJS( args = dict(cds_metadata = viewer_cds.cds_metadata, vi_name_input = self.vi_name_input, vi_filename_input = self.vi_filename_input, title=self.title, output_file_fields = self.output_file_fields), code=vi_name_code ) self.vi_name_input.js_on_change('value', self.vi_name_callback) def add_guidelines(self): #- Guidelines for VI flags vi_guideline_txt = "<B> VI guidelines </B>" vi_guideline_txt += "<BR /> <B> Classification flags: </B>" for flag in vi_flags : if flag['type'] == 'quality' : vi_guideline_txt += ("<BR />  [ "+flag['label']+" ] "+flag['description']) vi_guideline_txt += "<BR /> <B> Optional indications: </B>" for flag in vi_flags : if flag['type'] == 'issue' : vi_guideline_txt += ( "<BR />  [ " + flag['label'] + " (" + flag['shortlabel'] + ") ] " + flag['description'] ) vi_guideline_txt += "<BR /> <B> Comments: </B> <BR /> 100 characters max, avoid commas (automatically replaced by semi-columns), ASCII only." self.vi_guideline_div = Div(text=vi_guideline_txt) def add_vi_storage(self, viewer_cds, widgets): #- Save VI info to CSV file self.save_vi_button = Button(label="Download VI", button_type="success") save_vi_code = self.js_files["FileSaver.js"] + self.js_files["CSVtoArray.js"] + self.js_files["save_vi.js"] save_vi_code += """ download_vi_file(output_file_fields, cds_metadata.data, vi_filename_input.value) """ self.save_vi_callback = CustomJS( args = dict(cds_metadata = viewer_cds.cds_metadata, output_file_fields = self.output_file_fields, vi_filename_input = self.vi_filename_input), code=save_vi_code ) self.save_vi_button.js_on_event('button_click', self.save_vi_callback) #- Recover auto-saved VI data in browser self.recover_vi_button = Button(label="Recover auto-saved VI", button_type="default") recover_vi_code = self.js_files["CSVtoArray.js"] + self.js_files["recover_autosave_vi.js"] self.recover_vi_callback = CustomJS( args = dict(title=self.title, output_file_fields=self.output_file_fields, cds_metadata = viewer_cds.cds_metadata, ifiber = widgets.ifiberslider.value, vi_comment_input = self.vi_comment_input, vi_name_input=self.vi_name_input, vi_quality_input=self.vi_quality_input, vi_issue_input=self.vi_issue_input, vi_issue_slabels=self.vi_issue_slabels, vi_quality_labels=self.vi_quality_labels), code = recover_vi_code ) self.recover_vi_button.js_on_event('button_click', self.recover_vi_callback) #- Clear all auto-saved VI self.clear_vi_button = Button(label="Clear all auto-saved VI", button_type="default") self.clear_vi_callback = CustomJS( args = dict(), code = """ localStorage.clear() """ ) self.clear_vi_button.js_on_event('button_click', self.clear_vi_callback) def add_vi_table(self, viewer_cds): #- Show VI in a table vi_table_columns = [ TableColumn(field="VI_quality_flag", title="Flag", width=40), TableColumn(field="VI_issue_flag", title="Opt.", width=50), TableColumn(field="VI_z", title="VI z", width=50), TableColumn(field="VI_spectype", title="VI spectype", width=150), TableColumn(field="VI_comment", title="VI comment", width=200) ] self.vi_table = DataTable(source=viewer_cds.cds_metadata, columns=vi_table_columns, width=500) self.vi_table.height = 10 * self.vi_table.row_height def add_countdown(self, vi_countdown): #- VI countdown assert vi_countdown > 0 self.vi_countdown_callback = CustomJS(args=dict(vi_countdown=vi_countdown), code=""" if ( (cb_obj.label).includes('Start') ) { // Callback doesn't do anything after countdown started var countDownDate = new Date().getTime() + (1000 * 60 * vi_countdown); var countDownLoop = setInterval(function(){ var now = new Date().getTime(); var distance = countDownDate - now; if (distance<0) { cb_obj.label = "Time's up !"; clearInterval(countDownLoop); } else { var days = Math.floor(distance / (1000 * 60 * 60 * 24)); var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); var seconds = Math.floor((distance % (1000 * 60)) / 1000); //var stuff = days + "d " + hours + "h " + minutes + "m " + seconds + "s "; var stuff = minutes + "m " + seconds + "s "; cb_obj.label = "Countdown: " + stuff; } }, 1000); } """) self.vi_countdown_toggle = Toggle(label='Start countdown ('+str(vi_countdown)+' min)', active=False, button_type="success") self.vi_countdown_toggle.js_on_change('active', self.vi_countdown_callback)
def main(): st.set_page_config(page_title="Slide QR Code Generator", page_icon="icon.ico", layout='centered', initial_sidebar_state='auto') # layout st.title('Slide QR Code Generator') # col1 col1, col2, col3 = st.beta_columns(3) with col1: st.header("Step 1") st.subheader("Paste Data") copy_button = Button(label="Paste", max_width=100) copy_button.js_on_event( "button_click", CustomJS(code=""" navigator.clipboard.readText().then(text => document.dispatchEvent(new CustomEvent("GET_TEXT", {detail: text}))) """)) result = streamlit_bokeh_events(copy_button, events="GET_TEXT", key="get_text", refresh_on_update=False, override_height=75, debounce_time=0) # col2 with col2: st.header("Step 2") st.subheader("Set Configuration") # config qr_size_sb = st.slider("QR code size", 0, 100, 90) text_size_sb = st.slider("Label font size", 0, 20, 15) bg_width = st.slider("QR + label width", 0, 200, 110) bg_height = st.slider("QR + label height", 0, 200, 100) qr_pos_x_sb = st.slider("QR horizontal position", 0, 100, 10) qr_pos_y_sb = st.slider("QR vertical position", 0, 100, 0) text_pos_x_sb = st.slider("Label text horizontal position", 0, 100, 0) text_pos_y_sb = st.slider("Label text vertical position", 0, 100, 80) qr_size = qr_size_sb, qr_size_sb text_size = bg_width + 200, text_size_sb bg_size = bg_width, bg_height qr_pos = qr_pos_x_sb, qr_pos_y_sb text_pos = text_pos_x_sb, text_pos_y_sb kwargs = dict(qr_size=qr_size, text_size=text_size, bg_size=bg_size, qr_pos=qr_pos, text_pos=text_pos) # col3 with col3: st.header("Step 3") st.subheader("Right Click & Copy") if result: if "GET_TEXT" in result: # df = pd.read_csv(StringIO(result.get("GET_TEXT")), header=None) # df = pd.DataFrame(StringIO(result.get("GET_TEXT"))) data = result.get("GET_TEXT").split("\n") data = [i.rstrip() for i in data] df = pd.DataFrame(data) df.columns = ['slide_id'] st.write() # df['slide_id'] = df['slide_id'].apply(lambda x:x.replace("\n","")) # st.write(df['slide_id'][0]) # st.write(df['slide_id'][0]) # logic col1.write(f"Total number of slides: {len(df)}") col1.write(df) l = list(df['slide_id']) output = engine.create_many_qr_codes(l, kwargs) col3.image(output)
class ViewerWidgets(object): """ Encapsulates Bokeh widgets, and related callbacks, that are part of prospect's GUI. Except for VI widgets """ def __init__(self, plots, nspec): self.js_files = get_resources('js') self.navigation_button_width = 30 self.z_button_width = 30 self.plot_widget_width = (plots.plot_width+(plots.plot_height//2))//2 - 40 # used for widgets scaling #----- #- Ifiberslider and smoothing widgets # Ifiberslider's value controls which spectrum is displayed # These two widgets call update_plot(), later defined slider_end = nspec-1 if nspec > 1 else 0.5 # Slider cannot have start=end self.ifiberslider = Slider(start=0, end=slider_end, value=0, step=1, title='Spectrum (of '+str(nspec)+')') self.smootherslider = Slider(start=0, end=26, value=0, step=1.0, title='Gaussian Sigma Smooth') self.coaddcam_buttons = None self.model_select = None def add_navigation(self, nspec): #----- #- Navigation buttons self.prev_button = Button(label="<", width=self.navigation_button_width) self.next_button = Button(label=">", width=self.navigation_button_width) self.prev_callback = CustomJS( args=dict(ifiberslider=self.ifiberslider), code=""" if(ifiberslider.value>0 && ifiberslider.end>=1) { ifiberslider.value-- } """) self.next_callback = CustomJS( args=dict(ifiberslider=self.ifiberslider, nspec=nspec), code=""" if(ifiberslider.value<nspec-1 && ifiberslider.end>=1) { ifiberslider.value++ } """) self.prev_button.js_on_event('button_click', self.prev_callback) self.next_button.js_on_event('button_click', self.next_callback) def add_resetrange(self, viewer_cds, plots): #----- #- Axis reset button (superseeds the default bokeh "reset" self.reset_plotrange_button = Button(label="Reset X-Y range", button_type="default") reset_plotrange_code = self.js_files["adapt_plotrange.js"] + self.js_files["reset_plotrange.js"] self.reset_plotrange_callback = CustomJS(args = dict(fig=plots.fig, xmin=plots.xmin, xmax=plots.xmax, spectra=viewer_cds.cds_spectra), code = reset_plotrange_code) self.reset_plotrange_button.js_on_event('button_click', self.reset_plotrange_callback) def add_redshift_widgets(self, z, viewer_cds, plots): ## TODO handle "z" (same issue as viewerplots TBD) #----- #- Redshift / wavelength scale widgets z1 = np.floor(z*100)/100 dz = z-z1 self.zslider = Slider(start=-0.1, end=5.0, value=z1, step=0.01, title='Redshift rough tuning') self.dzslider = Slider(start=0.0, end=0.0099, value=dz, step=0.0001, title='Redshift fine-tuning') self.dzslider.format = "0[.]0000" self.z_input = TextInput(value="{:.4f}".format(z), title="Redshift value:") #- Observer vs. Rest frame wavelengths self.waveframe_buttons = RadioButtonGroup( labels=["Obs", "Rest"], active=0) self.zslider_callback = CustomJS( args=dict(zslider=self.zslider, dzslider=self.dzslider, z_input=self.z_input), code=""" // Protect against 1) recursive call with z_input callback; // 2) out-of-range zslider values (should never happen in principle) var z1 = Math.floor(parseFloat(z_input.value)*100) / 100 if ( (Math.abs(zslider.value-z1) >= 0.01) && (zslider.value >= -0.1) && (zslider.value <= 5.0) ){ var new_z = zslider.value + dzslider.value z_input.value = new_z.toFixed(4) } """) self.dzslider_callback = CustomJS( args=dict(zslider=self.zslider, dzslider=self.dzslider, z_input=self.z_input), code=""" var z = parseFloat(z_input.value) var z1 = Math.floor(z) / 100 var z2 = z-z1 if ( (Math.abs(dzslider.value-z2) >= 0.0001) && (dzslider.value >= 0.0) && (dzslider.value <= 0.0099) ){ var new_z = zslider.value + dzslider.value z_input.value = new_z.toFixed(4) } """) self.zslider.js_on_change('value', self.zslider_callback) self.dzslider.js_on_change('value', self.dzslider_callback) self.z_minus_button = Button(label="<", width=self.z_button_width) self.z_plus_button = Button(label=">", width=self.z_button_width) self.z_minus_callback = CustomJS( args=dict(z_input=self.z_input), code=""" var z = parseFloat(z_input.value) if(z >= -0.09) { z -= 0.01 z_input.value = z.toFixed(4) } """) self.z_plus_callback = CustomJS( args=dict(z_input=self.z_input), code=""" var z = parseFloat(z_input.value) if(z <= 4.99) { z += 0.01 z_input.value = z.toFixed(4) } """) self.z_minus_button.js_on_event('button_click', self.z_minus_callback) self.z_plus_button.js_on_event('button_click', self.z_plus_callback) self.zreset_button = Button(label='Reset to z_pipe') self.zreset_callback = CustomJS( args=dict(z_input=self.z_input, metadata=viewer_cds.cds_metadata, ifiberslider=self.ifiberslider), code=""" var ifiber = ifiberslider.value var z = metadata.data['Z'][ifiber] z_input.value = z.toFixed(4) """) self.zreset_button.js_on_event('button_click', self.zreset_callback) self.z_input_callback = CustomJS( args=dict(spectra = viewer_cds.cds_spectra, coaddcam_spec = viewer_cds.cds_coaddcam_spec, model = viewer_cds.cds_model, othermodel = viewer_cds.cds_othermodel, metadata = viewer_cds.cds_metadata, ifiberslider = self.ifiberslider, zslider = self.zslider, dzslider = self.dzslider, z_input = self.z_input, waveframe_buttons = self.waveframe_buttons, line_data = viewer_cds.cds_spectral_lines, lines = plots.speclines, line_labels = plots.specline_labels, zlines = plots.zoom_speclines, zline_labels = plots.zoom_specline_labels, overlap_waves = plots.overlap_waves, overlap_bands = plots.overlap_bands, fig = plots.fig ), code=""" var z = parseFloat(z_input.value) if ( z >=-0.1 && z <= 5.0 ) { // update zsliders only if needed (avoid recursive call) z_input.value = parseFloat(z_input.value).toFixed(4) var z1 = Math.floor(z*100) / 100 var z2 = z-z1 if ( Math.abs(z1-zslider.value) >= 0.01) zslider.value = parseFloat(parseFloat(z1).toFixed(2)) if ( Math.abs(z2-dzslider.value) >= 0.0001) dzslider.value = parseFloat(parseFloat(z2).toFixed(4)) } else { if (z_input.value < -0.1) z_input.value = (-0.1).toFixed(4) if (z_input.value > 5) z_input.value = (5.0).toFixed(4) } var line_restwave = line_data.data['restwave'] var ifiber = ifiberslider.value var waveshift_lines = (waveframe_buttons.active == 0) ? 1+z : 1 ; var waveshift_spec = (waveframe_buttons.active == 0) ? 1 : 1/(1+z) ; for(var i=0; i<line_restwave.length; i++) { lines[i].location = line_restwave[i] * waveshift_lines line_labels[i].x = line_restwave[i] * waveshift_lines zlines[i].location = line_restwave[i] * waveshift_lines zline_labels[i].x = line_restwave[i] * waveshift_lines } if (overlap_bands.length>0) { for (var i=0; i<overlap_bands.length; i++) { overlap_bands[i].left = overlap_waves[i][0] * waveshift_spec overlap_bands[i].right = overlap_waves[i][1] * waveshift_spec } } function shift_plotwave(cds_spec, waveshift) { var data = cds_spec.data var origwave = data['origwave'] var plotwave = data['plotwave'] if ( plotwave[0] != origwave[0] * waveshift ) { // Avoid redo calculation if not needed for (var j=0; j<plotwave.length; j++) { plotwave[j] = origwave[j] * waveshift ; } cds_spec.change.emit() } } for(var i=0; i<spectra.length; i++) { shift_plotwave(spectra[i], waveshift_spec) } if (coaddcam_spec) shift_plotwave(coaddcam_spec, waveshift_spec) // Update model wavelength array // NEW : don't shift model if othermodel is there if (othermodel) { var zref = othermodel.data['zref'][0] var waveshift_model = (waveframe_buttons.active == 0) ? (1+z)/(1+zref) : 1/(1+zref) ; shift_plotwave(othermodel, waveshift_model) } else if (model) { var zfit = 0.0 if(metadata.data['Z'] !== undefined) { zfit = metadata.data['Z'][ifiber] } var waveshift_model = (waveframe_buttons.active == 0) ? (1+z)/(1+zfit) : 1/(1+zfit) ; shift_plotwave(model, waveshift_model) } """) self.z_input.js_on_change('value', self.z_input_callback) self.waveframe_buttons.js_on_click(self.z_input_callback) self.plotrange_callback = CustomJS( args = dict( z_input=self.z_input, waveframe_buttons=self.waveframe_buttons, fig=plots.fig, ), code=""" var z = parseFloat(z_input.value) // Observer Frame if(waveframe_buttons.active == 0) { fig.x_range.start = fig.x_range.start * (1+z) fig.x_range.end = fig.x_range.end * (1+z) } else { fig.x_range.start = fig.x_range.start / (1+z) fig.x_range.end = fig.x_range.end / (1+z) } """ ) self.waveframe_buttons.js_on_click(self.plotrange_callback) # TODO: for record: is this related to waveframe bug? : 2 callbakcs for same click... def add_oii_widgets(self, plots): #------ #- Zoom on the OII doublet TODO mv js code to other file # TODO: is there another trick than using a cds to pass the "oii_saveinfo" ? # TODO: optimize smoothing for autozoom (current value: 0) cds_oii_saveinfo = ColumnDataSource( {'xmin':[plots.fig.x_range.start], 'xmax':[plots.fig.x_range.end], 'nsmooth':[self.smootherslider.value]}) self.oii_zoom_button = Button(label="OII-zoom", button_type="default") self.oii_zoom_callback = CustomJS( args = dict(z_input=self.z_input, fig=plots.fig, smootherslider=self.smootherslider, cds_oii_saveinfo=cds_oii_saveinfo), code = """ // Save previous setting (for the "Undo" button) cds_oii_saveinfo.data['xmin'] = [fig.x_range.start] cds_oii_saveinfo.data['xmax'] = [fig.x_range.end] cds_oii_saveinfo.data['nsmooth'] = [smootherslider.value] // Center on the middle of the redshifted OII doublet (vaccum) var z = parseFloat(z_input.value) fig.x_range.start = 3728.48 * (1+z) - 100 fig.x_range.end = 3728.48 * (1+z) + 100 // No smoothing (this implies a call to update_plot) smootherslider.value = 0 """) self.oii_zoom_button.js_on_event('button_click', self.oii_zoom_callback) self.oii_undo_button = Button(label="Undo OII-zoom", button_type="default") self.oii_undo_callback = CustomJS( args = dict(fig=plots.fig, smootherslider=self.smootherslider, cds_oii_saveinfo=cds_oii_saveinfo), code = """ fig.x_range.start = cds_oii_saveinfo.data['xmin'][0] fig.x_range.end = cds_oii_saveinfo.data['xmax'][0] smootherslider.value = cds_oii_saveinfo.data['nsmooth'][0] """) self.oii_undo_button.js_on_event('button_click', self.oii_undo_callback) def add_coaddcam(self, plots): #----- #- Highlight individual-arm or camera-coadded spectra coaddcam_labels = ["Camera-coadded", "Single-arm"] self.coaddcam_buttons = RadioButtonGroup(labels=coaddcam_labels, active=0) self.coaddcam_callback = CustomJS( args = dict(coaddcam_buttons = self.coaddcam_buttons, list_lines=[plots.data_lines, plots.noise_lines, plots.zoom_data_lines, plots.zoom_noise_lines], alpha_discrete = plots.alpha_discrete, overlap_bands = plots.overlap_bands, alpha_overlapband = plots.alpha_overlapband), code=""" var n_lines = list_lines[0].length for (var i=0; i<n_lines; i++) { var new_alpha = 1 if (coaddcam_buttons.active == 0 && i<n_lines-1) new_alpha = alpha_discrete if (coaddcam_buttons.active == 1 && i==n_lines-1) new_alpha = alpha_discrete for (var j=0; j<list_lines.length; j++) { list_lines[j][i].glyph.line_alpha = new_alpha } } var new_alpha = 0 if (coaddcam_buttons.active == 0) new_alpha = alpha_overlapband for (var j=0; j<overlap_bands.length; j++) { overlap_bands[j].fill_alpha = new_alpha } """ ) self.coaddcam_buttons.js_on_click(self.coaddcam_callback) def add_metadata_tables(self, viewer_cds, show_zcat=True, template_dicts=None, top_metadata=['TARGETID', 'EXPID']): """ Display object-related informations top_metadata: metadata to be highlighted in table_a Note: "short" CDS, with a single row, are used to fill these bokeh tables. When changing object, js code modifies these short CDS so that tables are updated. """ #- Sorted list of potential metadata: metadata_to_check = ['TARGETID', 'HPXPIXEL', 'TILEID', 'COADD_NUMEXP', 'COADD_EXPTIME', 'NIGHT', 'EXPID', 'FIBER', 'CAMERA', 'MORPHTYPE'] metadata_to_check += [ ('mag_'+x) for x in viewer_cds.phot_bands ] table_keys = [] for key in metadata_to_check: if key in viewer_cds.cds_metadata.data.keys(): table_keys.append(key) if 'NUM_'+key in viewer_cds.cds_metadata.data.keys(): for prefix in ['FIRST','LAST','NUM']: table_keys.append(prefix+'_'+key) if key in top_metadata: top_metadata.append(prefix+'_'+key) #- Table a: "top metadata" table_a_keys = [ x for x in table_keys if x in top_metadata ] self.shortcds_table_a, self.table_a = _metadata_table(table_a_keys, viewer_cds, table_width=600, shortcds_name='shortcds_table_a', selectable=True) #- Table b: Targeting information self.shortcds_table_b, self.table_b = _metadata_table(['Targeting masks'], viewer_cds, table_width=self.plot_widget_width, shortcds_name='shortcds_table_b', selectable=True) #- Table(s) c/d : Other information (imaging, etc.) remaining_keys = [ x for x in table_keys if x not in top_metadata ] if len(remaining_keys) > 7: table_c_keys = remaining_keys[0:len(remaining_keys)//2] table_d_keys = remaining_keys[len(remaining_keys)//2:] else: table_c_keys = remaining_keys table_d_keys = None self.shortcds_table_c, self.table_c = _metadata_table(table_c_keys, viewer_cds, table_width=self.plot_widget_width, shortcds_name='shortcds_table_c', selectable=False) if table_d_keys is None: self.shortcds_table_d, self.table_d = None, None else: self.shortcds_table_d, self.table_d = _metadata_table(table_d_keys, viewer_cds, table_width=self.plot_widget_width, shortcds_name='shortcds_table_d', selectable=False) #- Table z: redshift fitting information if show_zcat is not None : if template_dicts is not None : # Add other best fits fit_results = template_dicts[1] # Case of DeltaChi2 : compute it from Chi2s # The "DeltaChi2" in rr fits is between best fits for a given (spectype,subtype) # Convention: DeltaChi2 = -1 for the last fit. chi2s = fit_results['CHI2'][0] full_deltachi2s = np.zeros(len(chi2s))-1 full_deltachi2s[:-1] = chi2s[1:]-chi2s[:-1] cdsdata = dict(Nfit = np.arange(1,len(chi2s)+1), SPECTYPE = fit_results['SPECTYPE'][0], # [0:num_best_fits] (if we want to restrict... TODO?) SUBTYPE = fit_results['SUBTYPE'][0], Z = [ "{:.4f}".format(x) for x in fit_results['Z'][0] ], ZERR = [ "{:.4f}".format(x) for x in fit_results['ZERR'][0] ], ZWARN = fit_results['ZWARN'][0], CHI2 = [ "{:.1f}".format(x) for x in fit_results['CHI2'][0] ], DELTACHI2 = [ "{:.1f}".format(x) for x in full_deltachi2s ]) self.shortcds_table_z = ColumnDataSource(cdsdata, name='shortcds_table_z') columns_table_z = [ TableColumn(field=x, title=t, width=w) for x,t,w in [ ('Nfit','Nfit',5), ('SPECTYPE','SPECTYPE',70), ('SUBTYPE','SUBTYPE',60), ('Z','Z',50) , ('ZERR','ZERR',50), ('ZWARN','ZWARN',50), ('DELTACHI2','Δχ2(N+1/N)',70)] ] self.table_z = DataTable(source=self.shortcds_table_z, columns=columns_table_z, selectable=False, index_position=None, width=self.plot_widget_width) self.table_z.height = 3 * self.table_z.row_height else : self.shortcds_table_z, self.table_z = _metadata_table(viewer_cds.zcat_keys, viewer_cds, table_width=self.plot_widget_width, shortcds_name='shortcds_table_z', selectable=False) else : self.table_z = Div(text="Not available ") self.shortcds_table_z = None def add_specline_toggles(self, viewer_cds, plots): #----- #- Toggle lines self.speclines_button_group = CheckboxButtonGroup( labels=["Emission lines", "Absorption lines"], active=[]) self.majorline_checkbox = CheckboxGroup( labels=['Show only major lines'], active=[]) self.speclines_callback = CustomJS( args = dict(line_data = viewer_cds.cds_spectral_lines, lines = plots.speclines, line_labels = plots.specline_labels, zlines = plots.zoom_speclines, zline_labels = plots.zoom_specline_labels, lines_button_group = self.speclines_button_group, majorline_checkbox = self.majorline_checkbox), code=""" var show_emission = false var show_absorption = false if (lines_button_group.active.indexOf(0) >= 0) { // index 0=Emission in active list show_emission = true } if (lines_button_group.active.indexOf(1) >= 0) { // index 1=Absorption in active list show_absorption = true } for(var i=0; i<lines.length; i++) { if ( !(line_data.data['major'][i]) && (majorline_checkbox.active.indexOf(0)>=0) ) { lines[i].visible = false line_labels[i].visible = false zlines[i].visible = false zline_labels[i].visible = false } else if (line_data.data['emission'][i]) { lines[i].visible = show_emission line_labels[i].visible = show_emission zlines[i].visible = show_emission zline_labels[i].visible = show_emission } else { lines[i].visible = show_absorption line_labels[i].visible = show_absorption zlines[i].visible = show_absorption zline_labels[i].visible = show_absorption } } """ ) self.speclines_button_group.js_on_click(self.speclines_callback) self.majorline_checkbox.js_on_click(self.speclines_callback) def add_model_select(self, viewer_cds, template_dicts, num_approx_fits, with_full_2ndfit=True): #------ #- Select secondary model to display model_options = ['Best fit', '2nd best fit'] for i in range(1,1+num_approx_fits) : ith = 'th' if i==1 : ith='st' if i==2 : ith='nd' if i==3 : ith='rd' model_options.append(str(i)+ith+' fit (approx)') if with_full_2ndfit is False : model_options.remove('2nd best fit') for std_template in ['QSO', 'GALAXY', 'STAR'] : model_options.append('STD '+std_template) self.model_select = Select(value=model_options[0], title="Other model (dashed curve):", options=model_options) model_select_code = self.js_files["interp_grid.js"] + self.js_files["smooth_data.js"] + self.js_files["select_model.js"] self.model_select_callback = CustomJS( args = dict(ifiberslider = self.ifiberslider, model_select = self.model_select, fit_templates=template_dicts[0], cds_othermodel = viewer_cds.cds_othermodel, cds_model_2ndfit = viewer_cds.cds_model_2ndfit, cds_model = viewer_cds.cds_model, fit_results=template_dicts[1], std_templates=template_dicts[2], median_spectra = viewer_cds.cds_median_spectra, smootherslider = self.smootherslider, z_input = self.z_input, cds_metadata = viewer_cds.cds_metadata), code = model_select_code) self.model_select.js_on_change('value', self.model_select_callback) def add_update_plot_callback(self, viewer_cds, plots, vi_widgets, template_dicts): #----- #- Main js code to update plots update_plot_code = (self.js_files["adapt_plotrange.js"] + self.js_files["interp_grid.js"] + self.js_files["smooth_data.js"] + self.js_files["coadd_brz_cameras.js"] + self.js_files["update_plot.js"]) # TMP handling of template_dicts the_fit_results = None if template_dicts is None else template_dicts[1] # dirty self.update_plot_callback = CustomJS( args = dict( spectra = viewer_cds.cds_spectra, coaddcam_spec = viewer_cds.cds_coaddcam_spec, model = viewer_cds.cds_model, othermodel = viewer_cds.cds_othermodel, model_2ndfit = viewer_cds.cds_model_2ndfit, metadata = viewer_cds.cds_metadata, fit_results = the_fit_results, shortcds_table_z = self.shortcds_table_z, shortcds_table_a = self.shortcds_table_a, shortcds_table_b = self.shortcds_table_b, shortcds_table_c = self.shortcds_table_c, shortcds_table_d = self.shortcds_table_d, ifiberslider = self.ifiberslider, smootherslider = self.smootherslider, z_input = self.z_input, fig = plots.fig, imfig_source = plots.imfig_source, imfig_urls = plots.imfig_urls, model_select = self.model_select, vi_comment_input = vi_widgets.vi_comment_input, vi_std_comment_select = vi_widgets.vi_std_comment_select, vi_name_input = vi_widgets.vi_name_input, vi_quality_input = vi_widgets.vi_quality_input, vi_quality_labels = vi_widgets.vi_quality_labels, vi_issue_input = vi_widgets.vi_issue_input, vi_z_input = vi_widgets.vi_z_input, vi_category_select = vi_widgets.vi_category_select, vi_issue_slabels = vi_widgets.vi_issue_slabels ), code = update_plot_code ) self.smootherslider.js_on_change('value', self.update_plot_callback) self.ifiberslider.js_on_change('value', self.update_plot_callback)
S3.change.emit(); S4.change.emit(); S2.change.emit(); S5.change.emit(); """) button = Button(label="Delete entire noise (can't be undone)", button_type="success", callback=callback2) button.js_on_event(events.ButtonClick, ) callback3 = CustomJS(args=dict(S1=S1, S2=S2, S3=S3, S4=S4, S5=S5, s=slider), code=""" var data1 = S1.data; var data2 = S2.data; var data3 = S3.data; var data4 = S4.data; var data5 = S5.data; data2.RRSec_n_plus_1 = []; data2.RRSec = []; data2.epochTime = []; data2.beatChar = [];
def plotspectra(spectra, zcatalog=None, model=None, notebook=False, title=None): ''' TODO: document ''' if notebook: bk.output_notebook() #- If inputs are frames, convert to a spectra object if isinstance(spectra, list) and isinstance(spectra[0], desispec.frame.Frame): spectra = frames2spectra(spectra) frame_input = True else: frame_input = False if frame_input and title is None: meta = spectra.meta title = 'Night {} ExpID {} Spectrograph {}'.format( meta['NIGHT'], meta['EXPID'], meta['CAMERA'][1], ) #- Gather spectra into ColumnDataSource objects for Bokeh nspec = spectra.num_spectra() cds_spectra = list() for band in spectra.bands: #- Set masked bins to NaN so that Bokeh won't plot them bad = (spectra.ivar[band] == 0.0) | (spectra.mask[band] != 0) spectra.flux[band][bad] = np.nan cdsdata = dict( origwave=spectra.wave[band].copy(), plotwave=spectra.wave[band].copy(), ) for i in range(nspec): key = 'origflux' + str(i) cdsdata[key] = spectra.flux[band][i] cdsdata['plotflux'] = cdsdata['origflux0'] cds_spectra.append(bk.ColumnDataSource(cdsdata, name=band)) #- Reorder zcatalog to match input targets #- TODO: allow more than one zcatalog entry with different ZNUM per targetid targetids = spectra.target_ids() if zcatalog is not None: ii = np.argsort(np.argsort(targetids)) jj = np.argsort(zcatalog['TARGETID']) kk = jj[ii] zcatalog = zcatalog[kk] #- That sequence of argsorts may feel like magic, #- so make sure we got it right assert np.all(zcatalog['TARGETID'] == targetids) assert np.all(zcatalog['TARGETID'] == spectra.fibermap['TARGETID']) #- Also need to re-order input model fluxes if model is not None: mwave, mflux = model model = mwave, mflux[kk] #- Gather models into ColumnDataSource objects, row matched to spectra if model is not None: mwave, mflux = model model_obswave = mwave.copy() model_restwave = mwave.copy() cds_model_data = dict( origwave=mwave.copy(), plotwave=mwave.copy(), plotflux=np.zeros(len(mwave)), ) for i in range(nspec): key = 'origflux' + str(i) cds_model_data[key] = mflux[i] cds_model_data['plotflux'] = cds_model_data['origflux0'] cds_model = bk.ColumnDataSource(cds_model_data) else: cds_model = None #- Subset of zcatalog and fibermap columns into ColumnDataSource target_info = list() for i, row in enumerate(spectra.fibermap): target_bit_names = ' '.join(desi_mask.names(row['DESI_TARGET'])) txt = 'Target {}: {}'.format(row['TARGETID'], target_bit_names) if zcatalog is not None: txt += '<BR/>{} z={:.4f} ± {:.4f} ZWARN={}'.format( zcatalog['SPECTYPE'][i], zcatalog['Z'][i], zcatalog['ZERR'][i], zcatalog['ZWARN'][i], ) target_info.append(txt) cds_targetinfo = bk.ColumnDataSource(dict(target_info=target_info), name='targetinfo') if zcatalog is not None: cds_targetinfo.add(zcatalog['Z'], name='z') plot_width = 800 plot_height = 400 # tools = 'pan,box_zoom,wheel_zoom,undo,redo,reset,save' tools = 'pan,box_zoom,wheel_zoom,reset,save' fig = bk.figure(height=plot_height, width=plot_width, title=title, tools=tools, toolbar_location='above', y_range=(-10, 20)) fig.toolbar.active_drag = fig.tools[1] #- box zoom fig.toolbar.active_scroll = fig.tools[2] #- wheel zoom fig.xaxis.axis_label = 'Wavelength [Å]' fig.yaxis.axis_label = 'Flux' fig.xaxis.axis_label_text_font_style = 'normal' fig.yaxis.axis_label_text_font_style = 'normal' colors = dict(b='#1f77b4', r='#d62728', z='maroon') data_lines = list() for spec in cds_spectra: lx = fig.line('plotwave', 'plotflux', source=spec, line_color=colors[spec.name]) data_lines.append(lx) if cds_model is not None: model_lines = list() lx = fig.line('plotwave', 'plotflux', source=cds_model, line_color='black') model_lines.append(lx) legend = Legend(items=[ ("data", data_lines[-1::-1]), #- reversed to get blue as lengend entry ("model", model_lines), ]) else: legend = Legend(items=[ ("data", data_lines[-1::-1]), #- reversed to get blue as lengend entry ]) fig.add_layout(legend, 'center') fig.legend.click_policy = 'hide' #- or 'mute' #- Zoom figure around mouse hover of main plot zoomfig = bk.figure( height=plot_height // 2, width=plot_height // 2, y_range=fig.y_range, x_range=(5000, 5100), # output_backend="webgl", toolbar_location=None, tools=[]) for spec in cds_spectra: zoomfig.line('plotwave', 'plotflux', source=spec, line_color=colors[spec.name], line_width=1, line_alpha=1.0) if cds_model is not None: zoomfig.line('plotwave', 'plotflux', source=cds_model, line_color='black') #- Callback to update zoom window x-range zoom_callback = CustomJS(args=dict(zoomfig=zoomfig), code=""" zoomfig.x_range.start = cb_obj.x - 100; zoomfig.x_range.end = cb_obj.x + 100; """) fig.js_on_event(bokeh.events.MouseMove, zoom_callback) #----- #- Emission and absorption lines z = zcatalog['Z'][0] if (zcatalog is not None) else 0.0 line_data, lines, line_labels = add_lines(fig, z=z) #----- #- Add widgets for controling plots z1 = np.floor(z * 100) / 100 dz = z - z1 zslider = Slider(start=0.0, end=4.0, value=z1, step=0.01, title='Redshift') dzslider = Slider(start=0.0, end=0.01, value=dz, step=0.0001, title='+ Delta redshift') dzslider.format = "0[.]0000" #- Observer vs. Rest frame wavelengths waveframe_buttons = RadioButtonGroup(labels=["Obs", "Rest"], active=0) ifiberslider = Slider(start=0, end=nspec - 1, value=0, step=1) if frame_input: ifiberslider.title = 'Fiber' else: ifiberslider.title = 'Target' zslider_callback = CustomJS( args=dict( spectra=cds_spectra, model=cds_model, targetinfo=cds_targetinfo, ifiberslider=ifiberslider, zslider=zslider, dzslider=dzslider, waveframe_buttons=waveframe_buttons, line_data=line_data, lines=lines, line_labels=line_labels, fig=fig, ), #- TODO: reorder to reduce duplicated code code=""" var z = zslider.value + dzslider.value var line_restwave = line_data.data['restwave'] var ifiber = ifiberslider.value var zfit = 0.0 if(targetinfo.data['z'] != undefined) { zfit = targetinfo.data['z'][ifiber] } // Observer Frame if(waveframe_buttons.active == 0) { var x = 0.0 for(var i=0; i<line_restwave.length; i++) { x = line_restwave[i] * (1+z) lines[i].location = x line_labels[i].x = x } for(var i=0; i<spectra.length; i++) { var data = spectra[i].data var origwave = data['origwave'] var plotwave = data['plotwave'] for (var j=0; j<plotwave.length; j++) { plotwave[j] = origwave[j] } spectra[i].change.emit() } // Update model wavelength array if(model) { var origwave = model.data['origwave'] var plotwave = model.data['plotwave'] for(var i=0; i<plotwave.length; i++) { plotwave[i] = origwave[i] * (1+z) / (1+zfit) } model.change.emit() } // Rest Frame } else { for(i=0; i<line_restwave.length; i++) { lines[i].location = line_restwave[i] line_labels[i].x = line_restwave[i] } for (var i=0; i<spectra.length; i++) { var data = spectra[i].data var origwave = data['origwave'] var plotwave = data['plotwave'] for (var j=0; j<plotwave.length; j++) { plotwave[j] = origwave[j] / (1+z) } spectra[i].change.emit() } // Update model wavelength array if(model) { var origwave = model.data['origwave'] var plotwave = model.data['plotwave'] for(var i=0; i<plotwave.length; i++) { plotwave[i] = origwave[i] / (1+zfit) } model.change.emit() } } """) zslider.js_on_change('value', zslider_callback) dzslider.js_on_change('value', zslider_callback) waveframe_buttons.js_on_click(zslider_callback) plotrange_callback = CustomJS(args=dict( zslider=zslider, dzslider=dzslider, waveframe_buttons=waveframe_buttons, fig=fig, ), code=""" var z = zslider.value + dzslider.value // Observer Frame if(waveframe_buttons.active == 0) { fig.x_range.start = fig.x_range.start * (1+z) fig.x_range.end = fig.x_range.end * (1+z) } else { fig.x_range.start = fig.x_range.start / (1+z) fig.x_range.end = fig.x_range.end / (1+z) } """) waveframe_buttons.js_on_click(plotrange_callback) smootherslider = Slider(start=0, end=31, value=0, step=1.0, title='Gaussian Sigma Smooth') target_info_div = Div(text=target_info[0]) #----- #- Toggle lines lines_button_group = CheckboxButtonGroup(labels=["Emission", "Absorption"], active=[]) lines_callback = CustomJS(args=dict(line_data=line_data, lines=lines, line_labels=line_labels), code=""" var show_emission = false var show_absorption = false if (cb_obj.active.indexOf(0) >= 0) { // index 0=Emission in active list show_emission = true } if (cb_obj.active.indexOf(1) >= 0) { // index 1=Absorption in active list show_absorption = true } for(var i=0; i<lines.length; i++) { if(line_data.data['emission'][i]) { lines[i].visible = show_emission line_labels[i].visible = show_emission } else { lines[i].visible = show_absorption line_labels[i].visible = show_absorption } } """) lines_button_group.js_on_click(lines_callback) # lines_button_group.js_on_change('value', lines_callback) #----- update_plot = CustomJS(args=dict( spectra=cds_spectra, model=cds_model, targetinfo=cds_targetinfo, target_info_div=target_info_div, ifiberslider=ifiberslider, smootherslider=smootherslider, zslider=zslider, dzslider=dzslider, lines_button_group=lines_button_group, fig=fig, ), code=""" var ifiber = ifiberslider.value var nsmooth = smootherslider.value target_info_div.text = targetinfo.data['target_info'][ifiber] if(targetinfo.data['z'] != undefined) { var z = targetinfo.data['z'][ifiber] var z1 = Math.floor(z*100) / 100 zslider.value = z1 dzslider.value = (z - z1) } function get_y_minmax(pmin, pmax, data) { // copy before sorting to not impact original, and filter out NaN var dx = data.slice().filter(Boolean) dx.sort() var imin = Math.floor(pmin * dx.length) var imax = Math.floor(pmax * dx.length) return [dx[imin], dx[imax]] } // Smoothing kernel var kernel = []; for(var i=-2*nsmooth; i<=2*nsmooth; i++) { kernel.push(Math.exp(-(i**2)/(2*nsmooth))) } var kernel_offset = Math.floor(kernel.length/2) // Smooth plot and recalculate ymin/ymax // TODO: add smoother function to reduce duplicated code var ymin = 0.0 var ymax = 0.0 for (var i=0; i<spectra.length; i++) { var data = spectra[i].data var plotflux = data['plotflux'] var origflux = data['origflux'+ifiber] for (var j=0; j<plotflux.length; j++) { if(nsmooth == 0) { plotflux[j] = origflux[j] } else { plotflux[j] = 0.0 var weight = 0.0 // TODO: speed could be improved by moving `if` out of loop for (var k=0; k<kernel.length; k++) { var m = j+k-kernel_offset if((m >= 0) && (m < plotflux.length)) { var fx = origflux[m] if(fx == fx) { plotflux[j] = plotflux[j] + fx * kernel[k] weight += kernel[k] } } } plotflux[j] = plotflux[j] / weight } } spectra[i].change.emit() tmp = get_y_minmax(0.01, 0.99, plotflux) ymin = Math.min(ymin, tmp[0]) ymax = Math.max(ymax, tmp[1]) } // update model if(model) { var plotflux = model.data['plotflux'] var origflux = model.data['origflux'+ifiber] for (var j=0; j<plotflux.length; j++) { if(nsmooth == 0) { plotflux[j] = origflux[j] } else { plotflux[j] = 0.0 var weight = 0.0 // TODO: speed could be improved by moving `if` out of loop for (var k=0; k<kernel.length; k++) { var m = j+k-kernel_offset if((m >= 0) && (m < plotflux.length)) { var fx = origflux[m] if(fx == fx) { plotflux[j] = plotflux[j] + fx * kernel[k] weight += kernel[k] } } } plotflux[j] = plotflux[j] / weight } } model.change.emit() } // update y_range if(ymin<0) { fig.y_range.start = ymin * 1.4 } else { fig.y_range.start = ymin * 0.6 } fig.y_range.end = ymax * 1.4 """) smootherslider.js_on_change('value', update_plot) ifiberslider.js_on_change('value', update_plot) #----- #- Add navigation buttons navigation_button_width = 30 prev_button = Button(label="<", width=navigation_button_width) next_button = Button(label=">", width=navigation_button_width) prev_callback = CustomJS(args=dict(ifiberslider=ifiberslider), code=""" if(ifiberslider.value>0) { ifiberslider.value-- } """) next_callback = CustomJS(args=dict(ifiberslider=ifiberslider, nspec=nspec), code=""" if(ifiberslider.value<nspec+1) { ifiberslider.value++ } """) prev_button.js_on_event('button_click', prev_callback) next_button.js_on_event('button_click', next_callback) #----- slider_width = plot_width - 2 * navigation_button_width navigator = bk.Row( widgetbox(prev_button, width=navigation_button_width), widgetbox(next_button, width=navigation_button_width + 20), widgetbox(ifiberslider, width=slider_width - 20)) bk.show( bk.Column( bk.Row(fig, zoomfig), widgetbox(target_info_div, width=plot_width), navigator, widgetbox(smootherslider, width=plot_width // 2), bk.Row( widgetbox(waveframe_buttons, width=120), widgetbox(zslider, width=plot_width // 2 - 60), widgetbox(dzslider, width=plot_width // 2 - 60), ), widgetbox(lines_button_group), ))
[row.children.append(plot) for plot in plots] return row def make_tab(tab_nmb): if tab_nmb: return Panel(title='{name}'.format(name=tab_nmb), child=button) else: return Panel(title='{name}'.format(name=tab_nmb), child=get_plots()) [tabs_array.append(make_tab(tab_nmb)) for tab_nmb in range(2)] tabs = Tabs(tabs=tabs_array) def get_callback(val): return CustomJS(args=dict(val=val, tabs=tabs), code=""" if (val < 0) tabs.active = tabs.active + val > -1 ? tabs.active + val : tabs.tabs.length -1; if (val > 0) tabs.active = tabs.active + val < tabs.tabs.length ? tabs.active + val : 0;""" ) button.js_on_event(Tap, get_callback(-1)) button.on_click(button_callback) document = curdoc() document.add_root(tabs) show(tabs)
pdbs=pdbs) source.data = data update_data() download_data_button = Button(label="Download Current Data", button_type="success") download_data_button.js_on_event( events.ButtonClick, CustomJS(args=dict(source=source), code=""" var outputData = new Object(); for (var i = 0; i < source.data['pdbs'].length; i++) { outputData[source.data['gnames'][i]] = source.data['pdbs'][i] } const text = JSON.stringify(outputData); const blob = new Blob([text], { type: "text/plain;charset=utf-8;", }); saveAs(blob, 'graph_pdbs.json'); """)) inputs = WidgetBox( children=[scut, kcut, min_count, code_select, download_data_button]) hbox = HBox(children=[inputs, p]) # Configure hover tool and add the rectangles with the hover tool set up. boxes = p.rect(x='r_xs', y='r_ys',