class WordFreqsWidget:
    def __init__(self):
        self._word_filter_select = RadioGroup(
            labels=[word_filter.label for word_filter in WordFilter],
            active=1,
        )
        self._should_normalize = CheckboxGroup(
            labels=["Normalize word frequencies per day"], active=[])

        self.widget = column(
            self._word_filter_select,
            self._should_normalize,
            sizing_mode="stretch_width",
        )

    def on_change(self, *callbacks: Callable[[str, object, object],
                                             None]) -> None:
        self._word_filter_select.on_change("active", *callbacks)
        self._should_normalize.on_change("active", *callbacks)

    def set_enabled(self, enabled: bool) -> None:
        self._word_filter_select.disabled = not enabled
        self._should_normalize.disabled = not enabled

    @property
    def word_filter(self) -> WordFilter:
        for i, word_filter in enumerate(WordFilter):
            if i == self._word_filter_select.active:
                return word_filter
        raise ValueError()

    @property
    def should_normalize(self) -> bool:
        return bool(self._should_normalize.active)
示例#2
0
    def __init__(self,
                 flexname: str = "Default",
                 name: str = "OvioSlit",
                 display=fakedisplay,
                 width=200):  # BokehOVIOSlit::__init__()
        """Initialize this class."""
        #super().__init__()
        # (wg-python-property-variables)
        self.display = display
        self.flexname = flexname  # the name of the instrument for this instance
        self.name = name  # the name of the device in this instrument
        self.wwidth = width  # display width for its Bokeh widgets
        self.slit = "20"  # initial condition
        self.state = 'undefined'  # haven't started yet
        self.lamp = 0  # the illumination lamp associated
        self.validp = False  # wake up in false position
        self.spacer = Spacer(width=self.wwidth, height=5, background='black')
        self.slitlamp = RadioGroup(
            labels=[f"Illuminator Off", f"Illuminator On"],
            active=0,
            height=50,
            #orientation='horizontal',
            width=self.wwidth)

        self.slitchoices = Select(title=f"OVIO Slits",
                                  value='20',
                                  options=self.oviodropdowns,
                                  width=self.wwidth)
        self.slitchoices.on_change(
            'value',
            lambda attr, old, new: self.update_dropdown(attr, old, new))
        self.slitlamp.on_change(
            'active',
            lambda attr, old, new: self.radio_handler(attr, old, new))
        self.send_state()  # set the initial state.
示例#3
0
def addGeneLabels(fig, sourceData):
    labels = LabelSet(x='x_label',
                      y='y_label',
                      text='label',
                      source=sourceData,
                      render_mode='canvas',
                      text_font_size="18px")
    slider_font = Slider(start=0,
                         end=64,
                         value=16,
                         step=1,
                         title="Gene label font size in px")
    slider_angle = Slider(start=0,
                          end=pi / 2,
                          value=0,
                          step=0.01,
                          title="Gene label angle in radian")

    radio_label_type = RadioGroup(labels=[
        "name", "product", "family", "local identifier", "gene ID", "none"
    ],
                                  active=0)

    slider_angle.js_link('value', labels, 'angle')

    slider_font.js_on_change(
        'value',
        CustomJS(args=dict(other=labels),
                 code="other.text_font_size = this.value+'px';"))

    radio_label_type.js_on_click(
        CustomJS(args=dict(other=labels, source=sourceData),
                 code="""
                if(this.active == 5){
                    source.data['label'] = [];
                    for(var i=0;i<source.data['name'].length;i++){
                        source.data['label'].push('');
                    }
                }else if(this.active == 3){
                    source.data['label'] = source.data['gene_local_ID'];
                }else if(this.active == 4){
                    source.data['label'] = source.data['gene_ID'];
                }
                else{
                    source.data['label'] = source.data[this.labels[this.active]];
                }
                other.source = source;
                source.change.emit();
                """))

    label_header = Div(text="<b>Gene labels:</b>")
    radio_title = Div(text="""Gene labels to use:""", width=200, height=100)
    labels_block = column(label_header, row(slider_font, slider_angle),
                          column(radio_title, radio_label_type))

    fig.add_layout(labels)

    return labels_block, labels
示例#4
0
 def modify_doc(doc):
     source = ColumnDataSource(dict(x=[1, 2], y=[1, 1], val=["a", "b"]))
     plot = Plot(plot_height=400, plot_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.add_tools(CustomAction(callback=CustomJS(args=dict(s=source), code=RECORD("data", "s.data"))))
     group = RadioGroup(labels=LABELS, css_classes=["foo"])
     def cb(active):
         source.data['val'] = [active, "b"]
     group.on_click(cb)
     doc.add_root(column(group, plot))
示例#5
0
 def modify_doc(doc):
     source = ColumnDataSource(dict(x=[1, 2], y=[1, 1], val=["a", "b"]))
     plot = Plot(plot_height=400, plot_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.add_tools(CustomAction(callback=CustomJS(args=dict(s=source), code=RECORD("data", "s.data"))))
     group = RadioGroup(labels=LABELS, css_classes=["foo"])
     def cb(active):
         source.data['val'] = [active, "b"]
     group.on_click(cb)
     doc.add_root(column(group, plot))
    def __init__(self):
        self._word_filter_select = RadioGroup(
            labels=[word_filter.label for word_filter in WordFilter],
            active=1,
        )
        self._should_normalize = CheckboxGroup(
            labels=["Normalize word frequencies per day"], active=[])

        self.widget = column(
            self._word_filter_select,
            self._should_normalize,
            sizing_mode="stretch_width",
        )
示例#7
0
    def make_audio_panel():
        PLAY_TEST_AUDIO = """
        var path = 'Groove-Explorer-2/static/Test Audio/List Interface/'
        var index = selector.active;
        var labels = ['A', 'B', 'C', 'D', 'E'];
        var filename = path + labels[index] + '.mp3';
        audio_player.stop_audio();
        audio_player.play_audio(filename);
        """

        STOPCODE = """
        audio_player.stop_audio();
        """

        labels = ['A', 'B', 'C', 'D', 'E']
        audio_selector = RadioGroup(labels=labels,
                                    height_policy="auto",
                                    sizing_mode='scale_width',
                                    active=0)

        play_button = Button(label='Play')
        play_button.js_on_click(
            CustomJS(args=dict(selector=audio_selector), code=PLAY_TEST_AUDIO))
        stop_button = Button(label='Stop')
        stop_button.js_on_click(CustomJS(code=STOPCODE))
        audio_panel = column(audio_selector, play_button, stop_button)
        return audio_panel
示例#8
0
    def test_server_on_change_round_trip(self, bokeh_server_page: BokehServerPage) -> None:
        group = RadioGroup(labels=LABELS)
        def modify_doc(doc):
            source = ColumnDataSource(dict(x=[1, 2], y=[1, 1], val=["a", "b"]))
            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(attr, old, new):
                source.data['val'] = [new, "b"]
            group.on_change('active', cb)
            doc.add_root(column(group, plot))

        page = bokeh_server_page(modify_doc)

        el = find_element_for(page.driver, group, 'input[value="2"]')
        el.click()

        page.eval_custom_action()

        results = page.results
        assert results['data']['val'] == [2, "b"]

        el = find_element_for(page.driver, group, 'input[value="0"]')
        el.click()

        page.eval_custom_action()

        results = page.results
        assert results['data']['val'] == [0, "b"]
示例#9
0
    def _init_recorder(self):
        self.rec_button = Toggle(label=u"\u25CF  Record",
                                 button_type="default",
                                 active=False,
                                 width=210)
        self.file_name_widget = TextInput(value="test_file",
                                          title="File name:",
                                          width=210)
        self.file_type_widget = RadioGroup(labels=["EDF (BDF+)", "CSV"],
                                           active=0,
                                           width=210)

        columns = [
            widgets.TableColumn(
                field='timer',
                title="Record time",
                formatter=widgets.StringFormatter(text_align='center'))
        ]
        self.timer = widgets.DataTable(source=self._timer_source,
                                       index_position=None,
                                       sortable=False,
                                       reorderable=False,
                                       header_row=False,
                                       columns=columns,
                                       width=210,
                                       height=50,
                                       css_classes=["timer_widget"])

        self.rec_button.on_click(self._toggle_rec)
        return column(Spacer(width=210, height=5), self.file_name_widget,
                      self.file_type_widget, self.rec_button, self.timer)
示例#10
0
    def test_js_on_change_executes(self, bokeh_model_page) -> None:
        group = RadioGroup(labels=LABELS, css_classes=["foo"])
        group.js_on_click(CustomJS(code=RECORD("active", "cb_obj.active")))

        page = bokeh_model_page(group)

        el = page.driver.find_element_by_css_selector('.foo input[value="2"]')
        el.click()

        results = page.results
        assert results['active'] == 2

        el = page.driver.find_element_by_css_selector('.foo input[value="0"]')
        el.click()

        results = page.results
        assert results['active'] == 0

        assert page.has_no_console_errors()
示例#11
0
    def test_callback_property_executes(self, bokeh_model_page):
        group = RadioGroup(labels=LABELS, css_classes=["foo"])
        group.callback = CustomJS(code=RECORD("active", "cb_obj.active"))

        page = bokeh_model_page(group)

        el = page.driver.find_element_by_css_selector('.foo input[value="2"]')
        el.click()

        results = page.results
        assert results['active'] == 2

        el = page.driver.find_element_by_css_selector('.foo input[value="0"]')
        el.click()

        results = page.results
        assert results['active'] == 0

        assert page.has_no_console_errors()
示例#12
0
    def test_js_on_change_executes(self, bokeh_model_page: BokehModelPage) -> None:
        group = RadioGroup(labels=LABELS)
        group.js_on_change('active', CustomJS(code=RECORD("active", "cb_obj.active")))

        page = bokeh_model_page(group)

        el = find_element_for(page.driver, group, 'input[value="2"]')
        el.click()

        results = page.results
        assert results['active'] == 2

        el = find_element_for(page.driver, group, 'input[value="0"]')
        el.click()

        results = page.results
        assert results['active'] == 0

        assert page.has_no_console_errors()
示例#13
0
def addGeneTools(recs, sourceData):
    """ define tools to change the outline and fill colors of genes"""
    def colorStr(color_element):
        """javascript code to switch between partition, family and module color for the given 'color_element'"""
        return f"""
            if(this.active == 0){{
                source.data['{color_element}'] = source.data['partition_color'];
            }}else if(this.active == 1){{
                source.data['{color_element}'] = source.data['family_color'];
            }}else if(this.active == 2){{
                source.data['{color_element}'] = source.data['module_color'];
            }}
            recs.{color_element} = source.data['{color_element}'];
            source.change.emit();
        """

    radio_line_color = RadioGroup(labels=["partition", "family", "module"],
                                  active=0)
    radio_fill_color = RadioGroup(labels=["partition", "family", "module"],
                                  active=1)

    radio_line_color.js_on_click(
        CustomJS(args=dict(recs=recs, source=sourceData),
                 code=colorStr("line_color")))

    radio_fill_color.js_on_click(
        CustomJS(args=dict(recs=recs, source=sourceData),
                 code=colorStr("fill_color")))

    color_header = Div(text="<b>Genes:</b>")
    line_title = Div(text="""Color to use for gene outlines:""",
                     width=200,
                     height=100)
    fill_title = Div(text="""Color to fill genes with:""",
                     width=200,
                     height=100)

    gene_outline_size = Slider(start=0,
                               end=10,
                               value=5,
                               step=0.1,
                               title="Gene outline size:")
    gene_outline_size.js_on_change(
        'value',
        CustomJS(args=dict(other=recs),
                 code="""
                other.glyph.line_width = this.value;
                """))

    return column(
        color_header,
        row(column(line_title, radio_line_color),
            column(fill_title, radio_fill_color)), gene_outline_size)
示例#14
0
    def test_displays_options_list_of_string_labels_setting_inline(self, inline, bokeh_model_page: BokehModelPage) -> None:
        group = RadioGroup(labels=LABELS, inline=inline)
        page = bokeh_model_page(group)

        labels = find_elements_for(page.driver, group, "label")
        assert len(labels) == 3

        for i, label in enumerate(labels):
            assert label.text == LABELS[i]
            input = label.find_element(By.TAG_NAME, 'input')
            assert input.get_attribute('value') == str(i)
            assert input.get_attribute('type') == 'radio'
示例#15
0
    def test_callback_property_executes(self, bokeh_model_page):
        group = RadioGroup(labels=LABELS, css_classes=["foo"])
        group.callback = CustomJS(code=RECORD("active", "cb_obj.active"))

        page = bokeh_model_page(group)

        el = page.driver.find_element_by_css_selector(
            'div.foo div label input[value="2"]')
        el.click()

        results = page.results
        assert results['active'] == 2

        el = page.driver.find_element_by_css_selector(
            'div.foo div label input[value="0"]')
        el.click()

        results = page.results
        assert results['active'] == 0

        assert page.has_no_console_errors()
示例#16
0
    def __init__(self):
        pd.set_option('display.max_colwidth', -1)

        try:
            self.base_df = load_clean_df()
        except:
            fpath = BUNDLED_DATA_DIR + '/clean-light.csv'
            self.base_df = load_clean_df(fpath=fpath)
        self.base_df[SEARCH_FIELD] = self.base_df.apply(lambda row: ' '.join(str(x).lower() for x in row[SEARCH_COLS] if pd.notnull(x)), axis=1)

        # divs holding the charts
        self.sim_input_desc_div = Div(text='', width=CHARTS_WIDTH)
        self.sim_input_div = Div(text='', width=CHARTS_WIDTH)
        self.hm_divs = [Div(text='', width=DIV_WIDTH) for _ in range(2)]
        self.hist_divs = [Div(text='', width=DIV_WIDTH) for _ in range(2)]
        self.bc_divs = [Div(text='', width=DIV_WIDTH) for _ in range(2)]
        self.comp_div = Div(text='', width=CHARTS_WIDTH)

        # other divs
        self.top_div = Div(text='', width=CHARTS_WIDTH)

        # controls
        self.search_text_ctrl = TextInput(title=None, value='', width=WIDGET_BOX_WIDTH - 50)

        self.sim_ctrl = Select(title=None, options=all_sims.get_sim_names(), value=all_sims.get_sim_name(TfidfCosSim))
        self.analysed_cols_ctrl = CheckboxGroup(labels=COL_OPTIONS, active=[COL_OPTIONS.index(c) for c in ['suff_qtext', 'type']])
        self.sim_params_ctrl = CheckboxGroup(labels=list(SIM_PARAMS_CHECKOPTS), active=list(range(len(SIM_PARAMS_CHECKOPTS))))
        self.wv_dict_model_ctrl = RadioGroup(labels=[e.name for e in qsim.W2vModelName], active=0)

        self.sim_input_nres_ctrl = Slider(title="Number of results", value=10, start=1, end=100, step=1)
        self.sim_input_sample_size_ctrl = Slider(title="Sample size", value=1000, start=100, end=len(self.base_df), step=100)
        self.sim_input_text_ctrl = TextInput(title=None, value='', width=WIDGET_BOX_WIDTH - 50)

        self.hm_sample_size_ctrl = Slider(title=SAMPLE_SIZE_LABEL, value=30, start=10, end=50, step=5)

        self.hist_sample_size_ctrl = Slider(title=SAMPLE_SIZE_LABEL, value=30, start=10, end=len(self.base_df), step=10)

        self.bc_sample_size_ctrl = Slider(title=SAMPLE_SIZE_LABEL, value=30, start=10, end=len(self.base_df), step=5)
        self.bc_bars_ctrl = Slider(title="Number of bars", value=10, start=5, end=25, step=1)

        self.spectrum_start_ctrl = Slider(title="from", value=0, start=0, end=1, step=0.01)
        self.spectrum_end_ctrl = Slider(title="to", value=1, start=0, end=1, step=0.01)
        self.spectrum_buckets_ctrl = Slider(title="Number of buckets", value=5, start=1, end=20, step=1)
        self.spectrum_bucket_size_ctrl = Slider(title="Questions per bucket", value=2, start=1, end=10, step=1)
        self.spectrum_cs_only_ctrl = CheckboxGroup(labels=['Cross survey pairs only'], active=[])
        self.spectrum_spectrum_sample_size_ctrl = Slider(title=SAMPLE_SIZE_LABEL, value=30, start=30, end=len(self.base_df), step=10)

        self.submit_btn = Button(label="Update", button_type="success")
        self.submit_btn.on_click(self.update)

        self.update()
示例#17
0
    def test_displays_options_list_of_string_labels_setting_inline(self, inline, bokeh_model_page) -> None:
        group = RadioGroup(labels=LABELS, css_classes=["foo"], inline=inline)

        page = bokeh_model_page(group)

        el = page.driver.find_element_by_css_selector('.foo')

        labels = el.find_elements_by_tag_name('label')
        assert len(labels) == 3
        for i, label in enumerate(labels):
            assert label.text == LABELS[i]
            input = label.find_element_by_tag_name('input')
            assert input.get_attribute('value') == str(i)
            assert input.get_attribute('type') == 'radio'
示例#18
0
# ~ btn_rst: reset user selection of visible spectra
# =============================================================================
slider = Slider(start=0,
                end=rm.n_peaks - 1,
                value=0,
                step=1,
                title='Select a peak')
slider.on_change('value', peakSelector)

# n_features_input = RadioGroup(
#     labels=['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'], active=2, inline=True)
# n_features_input.on_change('active', featureCounter)
# n_features_input_title = Div(text="""# features """)

n_pca_input = RadioGroup(labels=['1', '2', '3', '4', '5'],
                         active=1,
                         inline=True)
n_pca_input.on_change('active', repredict)
n_pca_input_title = Div(text="""# pca:  """)

n_materials = RadioGroup(labels=['2', '3', '4', '5', '6', '7', '8', '9'],
                         active=1,
                         inline=True)
n_materials.on_change('active', repredict)
n_materials_title = Div(text="""# Materials """)

btn_rst = Button(label='reset')
btn_rst.on_click(selectSpectra_reset)
# =============================================================================
# ~ Layout setup
# =============================================================================
示例#19
0
文件: buttons.py 项目: zmj2008/bokeh
dropdown_split.js_on_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_click(
    CustomJS(
        code=
        "console.log('checkbox_group: active=' + this.active, this.toString())"
    ))

radio_group = RadioGroup(labels=["Option 1", "Option 2", "Option 3"], active=0)
radio_group.js_on_click(
    CustomJS(
        code=
        "console.log('radio_group: active=' + this.active, this.toString())"))

checkbox_button_group = CheckboxButtonGroup(
    labels=["Option 1", "Option 2", "Option 3"], active=[0, 1])
checkbox_button_group.js_on_click(
    CustomJS(
        code=
        "console.log('checkbox_button_group: active=' + this.active, this.toString())"
    ))

radio_button_group = RadioButtonGroup(
    labels=["Option 1", "Option 2", "Option 3"], active=0)
示例#20
0
def make_layout(data_filtered_kabir_part, injured_killed, cont_factor):
    def make_dataset(selected_options):
        by_factor_injured = pd.DataFrame(
            columns=['no_factors', 'no_victims', 'axis_type'])
        group_by = data_filtered_kabir_part.groupby(
            selected_options[1], as_index=False)[selected_options[0]].sum()
        #print(group_by)
        by_factor_injured['no_factors'] = group_by[selected_options[1]]
        by_factor_injured['no_victims'] = group_by[selected_options[0]]
        by_factor_injured['axis_type'] = selected_options[2]
        by_factor_injured['height'] = 450 + 5 * selected_options[3]
        by_factor_injured = by_factor_injured.sort_values(['no_victims'],
                                                          ascending=True)
        by_factor_range_injured = by_factor_injured.tail(selected_options[3])
        #print(by_factor_range_injured)

        return ColumnDataSource(by_factor_range_injured)

    def make_plot(src):
        # Blank plot in linear scale
        p_lin = figure(
            y_range=FactorRange(factors=list(src.data['no_factors'])),
            plot_width=700,
            plot_height=450,
            title='Bar plot of number of victims injured or killed',
            y_axis_label='Contributing factor',
            x_axis_label='No of victims',
            toolbar_location=None)
        glyph = HBar(y='no_factors',
                     right="no_victims",
                     left=0,
                     height=0.5,
                     fill_color="#460E61")
        p_lin.add_glyph(src, glyph)

        # Blank plot in log scale
        p_log = figure(
            y_range=FactorRange(factors=list(src.data['no_factors'])),
            plot_width=700,
            plot_height=450,
            title='Bar plot of no of victims injured or killed',
            y_axis_label='Contributing factor',
            x_axis_label='No of victims',
            x_axis_type='log',
            toolbar_location=None)

        glyph = HBar(y='no_factors',
                     right="no_victims",
                     left=0.00001,
                     height=0.5,
                     fill_color="#460E61")
        p_log.add_glyph(src, glyph)

        # Hover tool with hline mode
        hover = HoverTool(tooltips=[('Number of victims', '@no_victims'),
                                    ('Contributing Factor', '@no_factors')],
                          mode='hline')

        p_lin.add_tools(hover)
        p_log.add_tools(hover)

        return p_lin, p_log

    def update(attr, old, new):
        group_by_text = [
            select_inj_kill.value, select_factor.value, radio_group.active,
            factor_range.value
        ]
        #print(group_by_text)
        new_src = make_dataset(group_by_text)
        src.data.update(new_src.data)
        p_lin.y_range.factors = list(src.data['no_factors'])
        p_log.y_range.factors = list(src.data['no_factors'])
        #print(src.data['height'][0])
        p_lin.height = src.data['height'][0]
        p_log.height = src.data['height'][0]
        if src.data['axis_type'][0] == 0:
            p_log.visible = False
            p_lin.visible = True

        else:
            p_lin.visible = False
            p_log.visible = True

    # dropdown list
    select_inj_kill = Select(title="Select type of injury",
                             value="NUMBER OF PERSONS INJURED",
                             options=injured_killed)
    select_inj_kill.on_change('value', update)
    select_factor = Select(title="Contributing factor",
                           value="CONTRIBUTING FACTOR VEHICLE 1",
                           options=cont_factor)
    select_factor.on_change('value', update)

    ### radio group list
    div = Div(text="Choose Axis Type",
              style={
                  'font-size': '11pt',
                  'color': 'black',
                  'font-family': 'sans-serif'
              })
    radio_group = RadioGroup(labels=["Linear Scale", "Log Scale"], active=0)
    radio_group.on_change('active', update)

    factor_range = Slider(start=5,
                          end=65,
                          step=5,
                          value=10,
                          title='Contributing Factors')
    factor_range.on_change('value', update)

    ## initial selection
    initial_selections = [
        select_inj_kill.value, select_factor.value, radio_group.active,
        factor_range.value
    ]
    src = make_dataset(initial_selections)

    # creating plot
    p_lin, p_log = make_plot(src)
    p_log.visible = False

    scale_layout = column(div, radio_group)

    # Put controls in a single element
    controls = WidgetBox(select_inj_kill, select_factor, scale_layout,
                         factor_range)

    # Create a row layout
    layout = row(p_lin, p_log, controls)
    return layout
示例#21
0
    def __init__(self,
                 doc,
                 idx,
                 plots,
                 callback=None,
                 refresh_rate=500,
                 collection=None,
                 **kwargs):
        super().__init__(doc,
                         callback=callback,
                         refresh_rate=refresh_rate,
                         collection=collection,
                         **kwargs)

        self._countername_autocomplete = AutocompleteInput(
            name=f"Autocomplete_{BaseWidget.instance_num}",
            title="Countername:",
            completions=counternames,
            width=200,
        )

        self._collection_widget = DataCollectionSelect(doc,
                                                       self._set_collection,
                                                       width=120)
        self._selected_collection = None

        self._name = f"Line {idx}"
        self._name_edit = TextInput(title="Change name:",
                                    value=self._name,
                                    width=150)
        self._name_edit.on_change("value", self._change_name)
        self._title = Div(text=f"<h3>{self._name}</h3>")

        self._delete = Button(label="Remove", width=70, button_type="danger")
        self._delete.on_click(lambda: callback(idx))
        self._to_plot = Select(options=plots,
                               value=plots[0],
                               title="To plot:",
                               width=70)

        # Instance infos
        self._locality_input = TextInput(title="Locality #id:",
                                         value="0",
                                         width=70)
        self._locality_select = Select(options=[],
                                       title="Locality #id:",
                                       value="0",
                                       width=70)
        self._thread_id = TextInput(title="Worker #id:", width=70, value="0")
        self._pool = TextInput(title="Pool name:", width=70)
        self._pool_select = Select(options=[], title="Pool name:", width=70)
        self._is_total = RadioGroup(labels=["Yes", "No"], active=0, width=30)
        self._is_total.on_change("active", self._change_is_total)

        self._root = column(
            row(self._title, self._name_edit),
            self._delete,
            row(
                self._to_plot,
                self._collection_widget.layout(),
                self._countername_autocomplete,
                self._locality_input,
                self._pool,
                row(Div(text="Is total?"), self._is_total),
                empty_placeholder(),
            ),
        )
示例#22
0
    def __init__(
            self,
            options,
            name,
            data,
            data_types,
            type_categories,
    ):
        """

        Configuration options:
            - default/X Axis: name
            - default/Y Axis: name
            - default/Group by: name
            - default/Exclude: name1;name2;...;nameN
            - default/discard_axis_x: name1;name2;...;nameN
            - default/discard_axis_y: name1;name2;...;nameN
            - default/discard_group_by: name1;name2;...;nameN
            - default/discard_exclude_max_values: number. the maximum number of excluded values for a category to be shown. If an
                exclusion category has more values, no values added for this category
            - default/number_of_columns: int

        Args:
            options:
            name:
            data:
            data_types:
            type_categories:
        """
        self.discard_group_by = safe_lookup(options.config, name, 'default', 'discard_group_by', default='').split(';')
        self.discard_axis_x = safe_lookup(options.config, name, 'default', 'discard_axis_x', default='').split(';')
        self.discard_axis_y = safe_lookup(options.config, name, 'default', 'discard_axis_y', default='').split(';')
        self.discard_exclude_max_values = int(safe_lookup(options.config, name, 'default', 'discard_exclude_max_values', default='15'))

        self.group_by = CheckboxGroup()
        self.y_axis = CheckboxGroup()
        self.x_axis = RadioGroup()
        self.exclude = CheckboxGroup()

        controls_list = [
            PreText(text='Group by:'),
            self.group_by,
            PreText(text='Y Axis:'),
            self.y_axis,
            PreText(text='X Axis:'),
            self.x_axis,
            PreText(text='Exclude:'),
            self.exclude,
        ]
        controls = column(controls_list, name='PanelDataGraph_controls')
        controls.sizing_mode = "fixed"

        self.update_controls(options, name, data, data_types, type_categories)

        self.options = options
        self.layout = row(controls, sizing_mode='stretch_both')
        self.last_data = data
        self.last_data_types = data_types
        self.last_type_categories = type_categories
        self.last_figs_by_group = {}
        self.number_of_columns = int(safe_lookup(options.config, name, 'default', 'number_of_columns', default='2'))

        for control in controls_list:
            if hasattr(control, 'active'):
                control.on_change('active', lambda attr, old, new: self._update_and_clear_plots())

        self._update()

        super().__init__(ui=Panel(child=self.layout, title=name))
def plot(tables, output_filename):
    '''
    This is the plot function that uses Bokeh functions and widgets to make an interactive hexagon plot.

    This function recieves:
    - tables: dictionary with tables used to create arrays of repeated x, y coordinates (depending on the counts) for the hexagon plot.
    - output_filename: filename of .html output in the plots folder

    The coordinate arrays are used to create a pandas dataframe with Bokeh functions. This dataframe contains the q, r coordinates and counts used to plot the
    hexagons. To this dataframe, extra information is added (e.g. most common chemicals), which is displayed in the hover tooltip.

    Gaussian blur is added to copies of this dataframe and given as input to the Bokeh slider widget.
    Other widgets are added as well, for saturation, normalisation etc. Bokeh allows to customize these widges with javascript code.

    The hexagon plot is saved as a .html file and also shown in the browser.
    '''

    file_name = 'plots/' + str(output_filename) + '.html'
    output_file(file_name)

    # Blur and saturation values
    BLUR_MAX = 3
    BLUR_STEP_SIZE = 1
    SATURATION_MAX = 5
    SATURATION_STEP_SIZE = 0.25

    # First, create array for plot properties ( ratio, size of hexagons etc.)
    default_term = list(tables.keys())[0]
    x, y, ids = create_array(tables[default_term]['table'],
                             normalisation=False)

    # Hexagon plot properties
    length = len(x)
    orientation = 'flattop'
    ratio = ((max(y) - min(y)) / (max(x) - min(x)))
    size = 10 / ratio
    h = sqrt(3) * size
    h = h * ratio
    title = 'Hexbin plot for ' + str(
        length) + ' annotated chemicals with query ' + str(default_term)

    # make figure
    p = figure(title=title,
               x_range=[min(x) - 0.5, max(x) + 0.5],
               y_range=[0 - (h / 2), max(y) + 100],
               tools="wheel_zoom,reset,save",
               background_fill_color='#440154')

    p.grid.visible = False
    p.xaxis.axis_label = "log(P)"
    p.yaxis.axis_label = "mass in Da"
    p.xaxis.axis_label_text_font_style = 'normal'
    p.yaxis.axis_label_text_font_style = 'normal'

    # source for plot
    term_to_source, term_to_metadata, options = make_plot_sources(
        tables, size, ratio, orientation, BLUR_MAX, BLUR_STEP_SIZE)

    # start source for plot, this is the source that is first displayed in the hexagon figure
    x, y, ids = create_array(tables[default_term]['table'],
                             normalisation=False)
    df = hexbin(x, y, ids, size, aspect_scale=ratio, orientation=orientation)
    df = add_counts(df, tables[default_term]['table'])
    source = ColumnDataSource(df)
    metadata = term_to_metadata[default_term]
    metadata = return_html(metadata)

    # color mapper
    mapper = linear_cmap('scaling', 'Viridis256', 0,
                         max(source.data['scaling']))

    # plot
    hex = p.hex_tile(q="q",
                     r="r",
                     size=size,
                     line_color=None,
                     source=source,
                     aspect_scale=ratio,
                     orientation=orientation,
                     fill_color=mapper)

    # HOVER
    TOOLTIPS = return_tooltip()
    code_callback_hover = return_code('hover')
    callback_hover = CustomJS(code=code_callback_hover)
    hover = HoverTool(tooltips=TOOLTIPS,
                      callback=callback_hover,
                      show_arrow=False)
    p.add_tools(hover)

    # WIDGETS
    slider1 = Slider(start=1,
                     end=SATURATION_MAX,
                     value=1,
                     step=SATURATION_STEP_SIZE,
                     title="Saturation",
                     width=100)
    slider2 = Slider(start=0,
                     end=BLUR_MAX,
                     value=0,
                     step=BLUR_STEP_SIZE,
                     title="Blur",
                     width=100)
    checkbox = CheckboxGroup(labels=["TFIDF"], active=[])
    radio_button_group = RadioGroup(labels=["Viridis256", "Greys256"],
                                    active=0)
    button = Button(label="Metadata", button_type="default", width=100)
    multi_select = MultiSelect(title=output_filename,
                               value=[default_term],
                               options=options,
                               width=100,
                               height=300)

    # WIDGETS CODE FOR CALLBACK
    code_callback_slider1 = return_code('slider1')
    code_callback_slider2 = return_code('slider2')
    code_callback_checkbox = return_code('checkbox')
    code_callback_rbg = return_code('rbg')
    code_callback_button = return_code('button')
    code_callback_ms = return_code('multi_select')

    # WIDGETS CALLBACK
    callback_slider1 = CustomJS(args={
        'source': source,
        'mapper': mapper
    },
                                code=code_callback_slider1)
    callback_slider2 = CustomJS(args={
        'source': source,
        'mapper': mapper,
        'slider1': slider1,
        'multi_select': multi_select,
        'checkbox': checkbox,
        'term_to_source': term_to_source,
        'step_size': BLUR_STEP_SIZE
    },
                                code=code_callback_slider2)
    callback_checkbox = CustomJS(args={
        'source': source,
        'term_to_source': term_to_source,
        'multi_select': multi_select,
        'step_size': BLUR_STEP_SIZE,
        'slider1': slider1,
        'slider2': slider2,
        'mapper': mapper
    },
                                 code=code_callback_checkbox)
    callback_radio_button_group = CustomJS(args={
        'p': p,
        'mapper': mapper,
        'Viridis256': Viridis256,
        'Greys256': Greys256
    },
                                           code=code_callback_rbg)
    callback_button = CustomJS(args={
        'term_to_metadata': term_to_metadata,
        'multi_select': multi_select
    },
                               code=code_callback_button)
    callback_ms = CustomJS(args={
        'source': source,
        'term_to_source': term_to_source,
        'checkbox': checkbox,
        'metadata': metadata,
        'step_size': BLUR_STEP_SIZE,
        'slider2': slider2,
        'slider1': slider1,
        'p': p,
        'mapper': mapper
    },
                           code=code_callback_ms)

    # # WIDGETS INTERACTION
    slider1.js_on_change('value', callback_slider1)
    slider2.js_on_change('value', callback_slider2)
    checkbox.js_on_change('active', callback_checkbox)
    radio_button_group.js_on_change('active', callback_radio_button_group)
    button.js_on_event(events.ButtonClick, callback_button)
    multi_select.js_on_change("value", callback_ms)

    # LAYOUT
    layout = row(
        multi_select, p,
        column(slider1, slider2, checkbox, radio_button_group, button))

    show(layout)
    def __init__(self, image_views, disp_min=0, disp_max=1000, colormap="plasma"):
        """Initialize a colormapper.

        Args:
            image_views (ImageView): Associated streamvis image view instances.
            disp_min (int, optional): Initial minimal display value. Defaults to 0.
            disp_max (int, optional): Initial maximal display value. Defaults to 1000.
            colormap (str, optional): Initial colormap. Defaults to 'plasma'.
        """
        lin_colormapper = LinearColorMapper(
            palette=cmap_dict[colormap], low=disp_min, high=disp_max
        )

        log_colormapper = LogColorMapper(palette=cmap_dict[colormap], low=disp_min, high=disp_max)

        for image_view in image_views:
            image_view.image_glyph.color_mapper = lin_colormapper

        color_bar = ColorBar(
            color_mapper=lin_colormapper,
            location=(0, -5),
            orientation="horizontal",
            height=15,
            width=100,
            padding=5,
        )
        self.color_bar = color_bar

        # ---- selector
        def select_callback(_attr, _old, new):
            if new in cmap_dict:
                lin_colormapper.palette = cmap_dict[new]
                log_colormapper.palette = cmap_dict[new]
                high_color.color = cmap_dict[new][-1]

        select = Select(
            title="Colormap:", value=colormap, options=list(cmap_dict.keys()), default_size=100
        )
        select.on_change("value", select_callback)
        self.select = select

        # ---- auto toggle button
        def auto_toggle_callback(state):
            if state:
                display_min_spinner.disabled = True
                display_max_spinner.disabled = True
            else:
                display_min_spinner.disabled = False
                display_max_spinner.disabled = False

        auto_toggle = CheckboxGroup(labels=["Auto Colormap Range"], default_size=145)
        auto_toggle.on_click(auto_toggle_callback)
        self.auto_toggle = auto_toggle

        # ---- scale radiobutton group
        def scale_radiobuttongroup_callback(selection):
            if selection == 0:  # Linear
                for image_view in image_views:
                    image_view.image_glyph.color_mapper = lin_colormapper
                color_bar.color_mapper = lin_colormapper
                color_bar.ticker = BasicTicker()

            else:  # Logarithmic
                if self.disp_min > 0:
                    for image_view in image_views:
                        image_view.image_glyph.color_mapper = log_colormapper
                    color_bar.color_mapper = log_colormapper
                    color_bar.ticker = LogTicker()
                else:
                    scale_radiobuttongroup.active = 0

        scale_radiobuttongroup = RadioGroup(
            labels=["Linear", "Logarithmic"], active=0, default_size=145
        )
        scale_radiobuttongroup.on_click(scale_radiobuttongroup_callback)
        self.scale_radiobuttongroup = scale_radiobuttongroup

        # ---- display max value
        def display_max_spinner_callback(_attr, _old_value, new_value):
            self.display_min_spinner.high = new_value - STEP
            if new_value <= 0:
                scale_radiobuttongroup.active = 0

            lin_colormapper.high = new_value
            log_colormapper.high = new_value

        display_max_spinner = Spinner(
            title="Max Display Value:",
            low=disp_min + STEP,
            value=disp_max,
            step=STEP,
            disabled=bool(auto_toggle.active),
            default_size=145,
        )
        display_max_spinner.on_change("value", display_max_spinner_callback)
        self.display_max_spinner = display_max_spinner

        # ---- display min value
        def display_min_spinner_callback(_attr, _old_value, new_value):
            self.display_max_spinner.low = new_value + STEP
            if new_value <= 0:
                scale_radiobuttongroup.active = 0

            lin_colormapper.low = new_value
            log_colormapper.low = new_value

        display_min_spinner = Spinner(
            title="Min Display Value:",
            high=disp_max - STEP,
            value=disp_min,
            step=STEP,
            disabled=bool(auto_toggle.active),
            default_size=145,
        )
        display_min_spinner.on_change("value", display_min_spinner_callback)
        self.display_min_spinner = display_min_spinner

        # ---- high color
        def high_color_callback(_attr, _old_value, new_value):
            lin_colormapper.high_color = new_value
            log_colormapper.high_color = new_value

        high_color = ColorPicker(
            title="High Color:", color=cmap_dict[colormap][-1], default_size=90
        )
        high_color.on_change("color", high_color_callback)
        self.high_color = high_color

        # ---- mask color
        def mask_color_callback(_attr, _old_value, new_value):
            lin_colormapper.nan_color = new_value
            log_colormapper.nan_color = new_value

        mask_color = ColorPicker(title="Mask Color:", color="gray", default_size=90)
        mask_color.on_change("color", mask_color_callback)
        self.mask_color = mask_color
示例#25
0
    def __init__(self, sv_rt):
        """Initialize a stream control widget.
        """
        doc = curdoc()
        self.receiver = doc.receiver
        self.stats = doc.stats
        self.jf_adapter = doc.jf_adapter
        self._sv_rt = sv_rt

        # connect toggle button
        def toggle_callback(_active):
            if _active or not self._prev_image_buffer:
                self.prev_image_slider.disabled = True
            else:
                self.prev_image_slider.disabled = False

            self._update_toggle_view()

        toggle = Toggle(label="Connect", button_type="primary", tags=[True], default_size=145)
        toggle.js_on_change("tags", CustomJS(code=js_backpressure_code))
        toggle.on_click(toggle_callback)
        self.toggle = toggle

        # data type select
        datatype_select = Select(
            title="Data type:", value="Image", options=["Image", "Gains"], default_size=145
        )
        self.datatype_select = datatype_select

        # conversion options
        conv_opts_div = Div(text="Conversion options:", margin=(5, 5, 0, 5))
        conv_opts_cbg = CheckboxGroup(
            labels=["Mask", "Gap pixels", "Geometry"], active=[0, 1, 2], default_size=145
        )
        self.conv_opts_cbg = conv_opts_cbg
        self.conv_opts = column(conv_opts_div, conv_opts_cbg)

        # double pixels handling
        double_pixels_div = Div(text="Double pixels:", margin=(5, 5, 0, 5))
        double_pixels_rg = RadioGroup(labels=DP_LABELS, active=0, default_size=145)
        self.double_pixels_rg = double_pixels_rg
        self.double_pixels = column(double_pixels_div, double_pixels_rg)

        # rotate image select
        rotate_values = ["0", "90", "180", "270"]
        rotate_image = Select(
            title="Rotate image (deg):",
            value=rotate_values[0],
            options=rotate_values,
            default_size=145,
        )
        self.rotate_image = rotate_image

        # show only events
        self.show_only_events_toggle = CheckboxGroup(labels=["Show Only Events"], default_size=145)

        # Previous Image slider
        self._prev_image_buffer = deque(maxlen=60)

        def prev_image_slider_callback(_attr, _old, new):
            sv_rt.metadata, sv_rt.image = self._prev_image_buffer[new]
            # TODO: fix this workaround
            sv_rt.aggregated_image = sv_rt.image

        prev_image_slider = Slider(
            start=0, end=59, value_throttled=0, step=1, title="Previous Image", disabled=True,
        )
        prev_image_slider.on_change("value_throttled", prev_image_slider_callback)
        self.prev_image_slider = prev_image_slider

        doc.add_periodic_callback(self._update_toggle_view, 1000)
示例#26
0
def update():

    # Compute new y values: y
    y = np.sin(x) + np.random.random(N)

    # Update the ColumnDataSource data dictionary
    source.data = {'x': x, 'y': y}


# Add the update callback to the button
button.on_click(update)

# Create layout and add to current document
layout = column(widgetbox(button), plot)
curdoc().add_root(layout)

# Different types of buttons
# Import CheckboxGroup, RadioGroup, Toggle from bokeh.models
from bokeh.models import CheckboxGroup, RadioGroup, Toggle

# Add a Toggle: toggle
toggle = Toggle(button_type='success', label='Toggle button')

# Add a CheckboxGroup: checkbox
checkbox = CheckboxGroup(labels=['Option 1', 'Option 2', 'Option 3'])

# Add a RadioGroup: radio
radio = RadioGroup(labels=['Option 1', 'Option 2', 'Option 3'])

# Add widgetbox(toggle, checkbox, radio) to the current document
curdoc().add_root(widgetbox(toggle, checkbox, radio))
示例#27
0
class SelectCustomLine(BaseWidget):
    """Produces a widget for selecting a custom line (counter)"""
    def __init__(self,
                 doc,
                 idx,
                 plots,
                 callback=None,
                 refresh_rate=500,
                 collection=None,
                 **kwargs):
        super().__init__(doc,
                         callback=callback,
                         refresh_rate=refresh_rate,
                         collection=collection,
                         **kwargs)

        self._countername_autocomplete = AutocompleteInput(
            name=f"Autocomplete_{BaseWidget.instance_num}",
            title="Countername:",
            completions=counternames,
            width=200,
        )

        self._collection_widget = DataCollectionSelect(doc,
                                                       self._set_collection,
                                                       width=120)
        self._selected_collection = None

        self._name = f"Line {idx}"
        self._name_edit = TextInput(title="Change name:",
                                    value=self._name,
                                    width=150)
        self._name_edit.on_change("value", self._change_name)
        self._title = Div(text=f"<h3>{self._name}</h3>")

        self._delete = Button(label="Remove", width=70, button_type="danger")
        self._delete.on_click(lambda: callback(idx))
        self._to_plot = Select(options=plots,
                               value=plots[0],
                               title="To plot:",
                               width=70)

        # Instance infos
        self._locality_input = TextInput(title="Locality #id:",
                                         value="0",
                                         width=70)
        self._locality_select = Select(options=[],
                                       title="Locality #id:",
                                       value="0",
                                       width=70)
        self._thread_id = TextInput(title="Worker #id:", width=70, value="0")
        self._pool = TextInput(title="Pool name:", width=70)
        self._pool_select = Select(options=[], title="Pool name:", width=70)
        self._is_total = RadioGroup(labels=["Yes", "No"], active=0, width=30)
        self._is_total.on_change("active", self._change_is_total)

        self._root = column(
            row(self._title, self._name_edit),
            self._delete,
            row(
                self._to_plot,
                self._collection_widget.layout(),
                self._countername_autocomplete,
                self._locality_input,
                self._pool,
                row(Div(text="Is total?"), self._is_total),
                empty_placeholder(),
            ),
        )

    def _change_name(self, old, attr, new):
        self._name = new
        self._title.text = f"<h3>{new}</h3>"

    def _change_is_total(self, old, attr, new):
        if new:
            self._root.children[2].children[6] = self._thread_id
            self._pool.value = "default"
            if "default" in self._pool_select.options:
                self._pool_select.value = "default"
        else:
            self._pool.value = ""
            if "No pool" in self._pool_select.options:
                self._pool_select.value = "No pool"
            self._root.children[2].children[6] = empty_placeholder()

    def _set_collection(self, collection):
        self._selected_collection = collection
        if collection:
            self._countername_autocomplete.completions = collection.get_counter_names(
            )
            self._locality_select.options = collection.get_localities()
            self._pool_select.options = [
                "No pool" if not pool else pool
                for pool in collection.get_pools(self._locality_input.value)
            ]
            if "No pool" in self._pool_select.options:
                self._pool_select.value = "No pool"

            self._root.children[2].children[3] = self._locality_select
            self._root.children[2].children[4] = self._pool_select
        else:
            self._countername_autocomplete.completions = counternames
            self._root.children[2].children[3] = self._locality_input
            self._root.children[2].children[4] = self._pool

    def properties(self):
        """Returns a tuple containing all the information about the custom counter line.

        In order, returns:
            id of the plot
            collection object or None
            countername of the line
            instance
        """
        plot_id = int(self._to_plot.value.split()[1])

        countername = self._countername_autocomplete.value
        if not self._countername_autocomplete.value:
            countername = self._countername_autocomplete.value_input

        pool = None
        locality = "0"
        if self._selected_collection:
            locality = self._locality_select.value
            if self._pool_select.value != "No pool":
                pool = self._pool_select.value
        else:
            locality = self._locality_input.value
            if self._pool.value:
                pool = self._pool.value

        is_total = True
        if self._is_total.active == 1:
            is_total = False

        worker_id = None
        if is_total:
            worker_id = "total"
        else:
            worker_id = self._thread_id.value

        instance = format_instance(locality, pool, worker_id)

        return plot_id, self._selected_collection, countername, instance, self._name

    def set_properties(self, plot_id, collection, countername, locality_id,
                       pool, thread_id, name):
        """Sets the properties of the widget from the arguments"""
        if plot_id in self._to_plot.options:
            self._to_plot.value = plot_id

        self._set_collection(collection)
        self._countername_autocomplete.value = countername

        if locality_id in self._locality_select.options:
            self._locality_select.value = locality_id
        self._locality_input.value = locality_id

        if thread_id == "total":
            self._change_is_total(None, None, 0)
            self._is_total.active = 0
        else:
            self._thread_id.value = thread_id
            self._change_is_total(None, None, 1)
            self._is_total.active = 1

        if pool in self._pool_select.options:
            self._pool_select = pool
        self._pool.value = pool

        self._change_name(None, None, name)
        self._name_edit.value = name

    def set_plots(self, plots):
        self._to_plot.options = plots
        if self._to_plot.value not in plots:
            self._to_plot.value = plots[0]
示例#28
0
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(labels=["Option 1", "Option 2", "Option 3"], active=[0, 1], orientation="vertical")
radio_button_group_vertical = RadioButtonGroup(labels=["Option 1", "Option 2", "Option 3"], active=0, orientation="vertical")

text_input = TextInput(placeholder="Enter value ...")

completions = ["aaa", "aab", "aac", "baa", "caa"]
autocomplete_input = AutocompleteInput(placeholder="Enter value (auto-complete) ...", completions=completions)

text_area = TextAreaInput(placeholder="Enter text ...", cols=20, rows=10, value="uuu")

select = Select(options=["Option 1", "Option 2", "Option 3"])
示例#29
0
def plot_benchmarks_as_lines(title, *bm, transform=None,
            line_title_transform=None,
            logx=True, logy=True):
    import bokeh
    from bokeh.plotting import figure, output_file, show
    from bokeh.palettes import Dark2_5 as palette
    from bokeh.layouts import row, column
    from bokeh.models import (Legend, LegendItem, CheckboxGroup, CustomJS, Div,
                              RadioGroup, Toggle,
                              ColumnDataSource, DataTable, TableColumn)
    from bokeh.models.markers import marker_types
    import itertools
    #
    ids = entry_ids(*bm, transform=transform)
    colors = itertools.cycle(palette)
    markers = itertools.cycle(marker_types)
    p = figure(title=title,
               x_axis_type="log" if logx else "linear",
               y_axis_type="log" if logy else "linear",
               #background_fill_color="#fafafa",
               plot_width=1000,
               x_axis_label="Number of pixels",
               y_axis_label="Throughput (MB/s)",
               )
    p.toolbar.autohide = True
    #p.toolbar.active_inspect = [hover_tool, crosshair_tool]
    p.toolbar.active_drag = "auto"
    p.toolbar.active_scroll = "auto"
    #
    def dft(v): return v if v else (lambda n: n)
    tr = dft(transform)
    lttr = dft(line_title_transform)
    #
    for results in bm:
        x = [ids[name] for name in results.names]
        y = [bps/1e6 for bps in results.bytes_per_second]
        c = next(colors)
        marker = next(markers)
        next(markers)  # advance two
        line_name = lttr(results.first)
        #legends.append(LegendItem(name=c, label=line_name))
        p.scatter(x, y, marker=marker, size=8, color=c, legend_label=line_name)
        p.line(x, y,
               color=c, alpha=0.9,
               #muted_color=c, muted_alpha=0.05,
               legend_label=line_name)
    p.legend.click_policy = "hide"
    p.legend.label_text_font_size = "10px"
    #
    def input_title(title):
        return Div(text=f"<h3>{title}</h3>")
    inputs = []
    first = bm[0].first.meta
    for k, g in first.checkbox_groups().items():
        cb = CheckboxGroup(labels=[str(v) for v in g],
                           active=[i for i in range(len(g))],
                           inline=True)
        inputs.append(input_title(k))
        inputs.append(cb)
    #
    # https://github.com/bokeh/bokeh/blob/branch-2.3/examples/app/export_csv/main.py
    x_axis_values = [f"{m.num_pixels}px" for m in bm[0].meta]
    table_sources = []
    for i, px in enumerate(x_axis_values):
        c = ColumnDataSource(data={
            'name': [nth(results.filtered_names, i) for results in bm],
            'bytes_per_second': [nth(results.bytes_per_second, i) for results in bm],
            'items_per_second': [nth(results.items_per_second, i) for results in bm],
            'cpu_time': [nth(results.real_time, i) for results in bm],
            'real_time': [nth(results.real_time, i) for results in bm],
            'iterations': [nth(results.iterations, i) for results in bm],
            'threads': [nth(results.threads, i) for results in bm],
        })
        table_sources.append(c)
    selected_x_index = 8  # FIXME (currently 2000 pixels)
    table_source = copy.deepcopy(table_sources[selected_x_index])
    relvalues = Toggle(label="Table: Relative values")
    px_title = input_title("Table: number of pixels")
    px_radiogroup = RadioGroup(labels=x_axis_values, active=selected_x_index)
    table_inputs = [relvalues, px_title, px_radiogroup]
    #
    table_cols = [
        TableColumn(field='name', title='Name'),
        TableColumn(field='bytes_per_second', title='Bytes/second'),
        TableColumn(field='items_per_second', title='Items/second'),
        TableColumn(field='cpu_time', title='CPU time'),
        TableColumn(field='real_time', title='Real time'),
        TableColumn(field='iterations', title='Iterations'),
        TableColumn(field='threads', title='Threads'),
    ]
    data_table = DataTable(source=table_source, columns=table_cols, width=1200)
    callback = CustomJS(args=dict(
        radiogroup=px_radiogroup,
        source=table_source,
        table=table_sources
    ), code="""
    console.log(`active=${radiogroup.active}`);
    /*source.data=table[radiogroup.active];*/
    var nrows = source.data['name'].length;
    var ts = table[radiogroup.active].data;
    var names = ["name", "bytes_per_second", "items_per_second", "cpu_time", "real_time", "iterations", "threads"];
    var ncols = names.length;
    console.log(`names=${names} nrows=${nrows} ncols=${ncols}`);
    for(var i = 0; i < nrows; i++) {
        for(var j = 0; j < ncols; ++j) {
           var name = names[j];
           /*console.log(`i=${i} j=${j} name=${name}`);*/
           source.data[name][i] = ts[name][i];
        }
    }
    source.change.emit();
    """)
    px_radiogroup.js_on_change('active', callback)
    #                        lambda attr, old, new: log(f"attr={attr} old={old} new={new} active={px_radiogroup.active}"))
    #
    layout = column(
        row(column(*inputs), p),
        row(column(*table_inputs), data_table))
    show(layout)
示例#30
0
    'Orange',
]

df = pd.DataFrame({'x': x, 'y': y, 'label': label})

source = ColumnDataSource(data=dict(x=df.x, y=df.y, label=df.label))

plot_figure = figure(title='Radio Group',
                     height=450,
                     width=600,
                     tools="save,reset",
                     toolbar_location="below")

plot_figure.scatter('x', 'y', color='label', source=source, size=10)

radio_group = RadioGroup(labels=["Red", "Orange"])


def radiogroup_click(attr, old, new):
    active_radio = radio_group.active  ##Getting radio button value

    # filter the dataframe with value in radio-button
    if active_radio == 0:
        selected_df = df[df['label'] == 'Red']
    elif active_radio == 1:
        selected_df = df[df['label'] == "Orange"]

    source.data = dict(x=selected_df.x,
                       y=selected_df.y,
                       label=selected_df.label)
        button = Button(label='press me')

        def update():
            # Do something interesting
        
        button.on_click(update)

        # Button types

            from bokeh.models import CheckboxGroup, RadioGroup, Toggle

            toggle = Toggle(label='Some on/off', button_type='success')

            checkbox = CheckboxGroup(labels=['foo', 'bar', 'baz'])

            radio = RadioGroup(labels=['2000', '2010', '2020'])

            def callback(active)
                # Active tells which button is active

            curdoc().add_root(widgetbox(toggle, checkbox, radio))

'''Case study'''

    # EDA

        from bokeh.io import output_file, show
        from bokeh.plotting import figure
        from bokeh.models import HoverTool, ColumnDataSource

        source = ColumnDataSource(data={
def bkapp(doc):
    
### Functions ###

    # functions for user dialogs

    def open_file(ftype):
        root = Tk()
        root.withdraw()
        file = askopenfilename(filetypes=ftype,
                               title='Open File',
                               initialdir=os.getcwd())
        root.destroy()
        return file
    
    def choose_directory():
        root = Tk()
        root.withdraw()
        out_dir = askdirectory()
        root.destroy()
        return out_dir

    def write_output_directory(output_dir):
        root = Tk()
        root.withdraw()
        makeDir = askquestion('Make Directory','Output directory not set. Make directory: '
            +output_dir+'? If not, you\'ll be prompted to change directories.',icon = 'warning')
        root.destroy()
        return makeDir

    def overwrite_file():
        root = Tk()
        root.withdraw()
        overwrite = askquestion('Overwrite File','File already exits. Do you want to overwrite?',icon = 'warning')
        root.destroy()
        return overwrite

    def update_filename():
        filetype = [("Video files", "*.mp4")]
        fname = open_file(filetype)
        if fname:
            #print('Successfully loaded file: '+fname)
            load_data(filename=fname)         

    def change_directory():
        out_dir = choose_directory()
        if out_dir:
            source.data["output_dir"] = [out_dir]
            outDir.text = out_dir
        return out_dir

    # load data from file

    def load_data(filename):
        vidcap = cv2.VideoCapture(filename)
        success,frame = vidcap.read()
        img_tmp,_,__ = cv2.split(frame)
        h,w = np.shape(img_tmp)
        img = np.flipud(img_tmp)
        radio_button_gp.active = 0
        fname = os.path.split(filename)[1]
        input_dir = os.path.split(filename)[0]
        if source.data['output_dir'][0]=='':
            output_dir = os.path.join(input_dir,fname.split('.')[0])
        else:
            output_dir = source.data['output_dir'][0]
        if not os.path.isdir(output_dir):
            makeDir = write_output_directory(output_dir)
            if makeDir=='yes':
                os.mkdir(output_dir)
            else:
                output_dir = change_directory()
        if output_dir:
            source.data = dict(image_orig=[img], image=[img], bin_img=[0],
                x=[0], y=[0], dw=[w], dh=[h], num_contours=[0], roi_coords=[0], 
                img_name=[fname], input_dir=[input_dir], output_dir=[output_dir])
            curr_img = p.select_one({'name':'image'})
            if curr_img:
                p.renderers.remove(curr_img)
            p.image(source=source, image='image', x='x', y='y', dw='dw', dh='dh', color_mapper=cmap,level='image',name='image')      
            p.plot_height=int(h/2)
            p.plot_width=int(w/2)
            #p.add_tools(HoverTool(tooltips=IMG_TOOLTIPS))
            inFile.text = fname
            outDir.text = output_dir
        else:
            print('Cancelled. To continue please set output directory.{:<100}'.format(' '),end="\r")

    # resetting sources for new data or new filters/contours

    def remove_plot():
        source.data["num_contours"]=[0]
        contours_found.text = 'Droplets Detected: 0'
        source_contours.data = dict(xs=[], ys=[])
        source_label.data = dict(x=[], y=[], label=[])

    # apply threshold filter and display binary image

    def apply_filter():
        if source.data['input_dir'][0] == '':
            print('No image loaded! Load image first.{:<100}'.format(' '),end="\r")
        else:
            img = np.squeeze(source.data['image_orig'])
            # remove contours if present
            if source.data["num_contours"]!=[0]:
                remove_plot()
            if radio_button_gp.active == 1:
                thresh = filters.threshold_otsu(img)
                binary = img > thresh
                bin_img = binary*255
                source.data["bin_img"] = [bin_img]
            elif radio_button_gp.active == 2:
                thresh = filters.threshold_isodata(img)
                binary = img > thresh
                bin_img = binary*255
                source.data["bin_img"] = [bin_img]
            elif radio_button_gp.active == 3:
                thresh = filters.threshold_mean(img)
                binary = img > thresh
                bin_img = binary*255
                source.data["bin_img"] = [bin_img]
            elif radio_button_gp.active == 4:
                thresh = filters.threshold_li(img)
                binary = img > thresh
                bin_img = binary*255
                source.data["bin_img"] = [bin_img]
            elif radio_button_gp.active == 5:
                thresh = filters.threshold_yen(img)
                binary = img > thresh
                bin_img = binary*255
                source.data["bin_img"] = [bin_img]
            elif radio_button_gp.active == 6:
                off = offset_spinner.value
                block_size = block_spinner.value
                thresh = filters.threshold_local(img,block_size,offset=off)
                binary = img > thresh
                bin_img = binary*255
                source.data["bin_img"] = [bin_img]
            else:
                bin_img = img
            source.data['image'] = [bin_img]

    # image functions for adjusting the binary image
    
    def close_img():
        if source.data["num_contours"]!=[0]:
            remove_plot()
        if radio_button_gp.active == 0:
            print("Please Select Filter for Threshold{:<100}".format(' '),end="\r")
        else:
            source.data["image"] = source.data["bin_img"]
            img = np.squeeze(source.data['bin_img'])
            closed_img = binary_closing(255-img)*255
            source.data['image'] = [255-closed_img]
            source.data['bin_img'] = [255-closed_img]

    def dilate_img():
        if source.data["num_contours"]!=[0]:
            remove_plot()
        if radio_button_gp.active == 0:
            print("Please Select Filter for Threshold{:<100}".format(' '),end="\r")
        else:
            img = np.squeeze(source.data['bin_img'])
            dilated_img = binary_dilation(255-img)*255
            source.data['image'] = [255-dilated_img]
            source.data['bin_img'] = [255-dilated_img]

    def erode_img():
        if source.data["num_contours"]!=[0]:
            remove_plot()
        if radio_button_gp.active == 0:
            print("Please Select Filter for Threshold{:<100}".format(' '),end="\r")
        else:
            img = np.squeeze(source.data['bin_img'])
            eroded_img = binary_erosion(255-img)*255
            source.data['image'] = [255-eroded_img] 
            source.data['bin_img'] = [255-eroded_img]  

    # the function for identifying closed contours in the image

    def find_contours(level=0.8):
        min_drop_size = contour_rng_slider.value[0]
        max_drop_size = contour_rng_slider.value[1]
        min_dim = 20
        max_dim = 200
        if radio_button_gp.active == 0:
            print("Please Select Filter for Threshold{:<100}".format(' '),end="\r")
        elif source.data['input_dir'][0] == '':
            print('No image loaded! Load image first.{:<100}'.format(' '),end="\r")
        else:
            img = np.squeeze(source.data['bin_img'])
            h,w = np.shape(img)        
            contours = measure.find_contours(img, level)
            length_cnt_x = [cnt[:,1] for cnt in contours if np.shape(cnt)[0] < max_drop_size 
                             and np.shape(cnt)[0] > min_drop_size]
            length_cnt_y = [cnt[:,0] for cnt in contours if np.shape(cnt)[0] < max_drop_size 
                             and np.shape(cnt)[0] > min_drop_size]
            matched_cnt_x = []
            matched_cnt_y = []
            roi_coords = []
            label_text = []
            label_y = np.array([])
            count=0
            for i in range(len(length_cnt_x)):
                cnt_x = length_cnt_x[i]
                cnt_y = length_cnt_y[i]
                width = np.max(cnt_x)-np.min(cnt_x)
                height = np.max(cnt_y)-np.min(cnt_y)
                if width>min_dim and width<max_dim and height>min_dim and height<max_dim:
                    matched_cnt_x.append(cnt_x)
                    matched_cnt_y.append(cnt_y)
                    roi_coords.append([round(width),round(height),round(np.min(cnt_x)),round(h-np.max(cnt_y))])
                    label_text.append(str(int(count)+1))
                    label_y = np.append(label_y,np.max(cnt_y))
                    count+=1
            curr_contours = p.select_one({'name':'overlay'})
            if curr_contours:
                p.renderers.remove(curr_contours)
            #if source.data["num_contours"]==[0]:
                #remove_plot()
                #p.image(source=source, image='image_orig', x=0, y=0, dw=w, dh=h, color_mapper=cmap, name='overlay',level='underlay')      
            source.data["image"] = source.data["image_orig"]
            source.data["num_contours"] = [count]
            #source.data["cnt_x"] = [matched_cnt_x]
            #source.data["cnt_y"] = [matched_cnt_y]
            source.data["roi_coords"] = [roi_coords]
            source_contours.data = dict(xs=matched_cnt_x, ys=matched_cnt_y)
            p.multi_line(xs='xs',ys='ys',source=source_contours, color=(255,127,14),line_width=2, name="contours",level='glyph')
            if len(np.array(roi_coords).shape)<2:
                if len(np.array(roi_coords)) <4:
                    print('No contours found. Try adjusting parameters or filter for thresholding.{:<100}'.format(' '),end="\r")
                    source_label.data = dict(x=[],y=[],label=[])
                else:
                    source_label.data = dict(x=np.array(roi_coords)[2], y=label_y, label=label_text)
            else:
                source_label.data = dict(x=np.array(roi_coords)[:,2], y=label_y, label=label_text)
            contours_found.text = 'Droplets Detected: '+str(len(matched_cnt_x))

    # write the contours and parameters to files

    def export_ROIs():
        if source.data["num_contours"]==[0]:
            print("No Contours Found! Find contours first.{:<100}".format(' '),end="\r")
        else:
            hdr = 'threshold filter,contour min,contour max'
            thresh_filter = radio_button_gp.active
            cnt_min = contour_rng_slider.value[0]
            cnt_max = contour_rng_slider.value[1]
            params = [thresh_filter,cnt_min,cnt_max]
            if radio_button_gp.active == 6:
                off = offset_spinner.value
                block_size = block_spinner.value
                hdr = hdr + ',local offset,local block size'
                params.append(off,block_size)
            params_fname = 'ContourParams.csv'
            params_out = os.path.join(source.data['output_dir'][0],params_fname)
            overwrite = 'no'
            if os.path.exists(params_out):
                overwrite = overwrite_file()
            if overwrite == 'yes' or not os.path.exists(params_out):
                np.savetxt(params_out,np.array([params]),delimiter=',',header=hdr,comments='')
            roi_coords = np.array(source.data["roi_coords"][0])
            out_fname = 'ROI_coords.csv'
            out_fullpath = os.path.join(source.data['output_dir'][0],out_fname)
            if overwrite == 'yes' or not os.path.exists(out_fullpath):
                hdr = 'width,height,x,y'
                np.savetxt(out_fullpath,roi_coords,delimiter=',',header=hdr,comments='')
                print('Successfully saved ROIs coordinates as: '+out_fullpath,end='\r')
                source.data['roi_coords'] = [roi_coords]

    # function for loading previously made files or error handling for going out of order

    def check_ROI_files():
        coords_file = os.path.join(source.data["output_dir"][0],'ROI_coords.csv')
        n_cnt_curr = source.data["num_contours"][0]
        roi_coords_curr = source.data["roi_coords"][0]
        if os.path.exists(coords_file):
            df_tmp=pd.read_csv(coords_file, sep=',')
            roi_coords = np.array(df_tmp.values)
            n_cnt = len(roi_coords)
            if n_cnt_curr != n_cnt or not np.array_equal(roi_coords_curr,roi_coords):
                print('Current ROIs are different from saved ROI file! ROIs from saved file will be used instead and plot updated.',end="\r")
            source.data["num_contours"] = [n_cnt]
            source.data["roi_coords"] = [roi_coords]
            params_file = os.path.join(source.data['output_dir'][0],'ContourParams.csv')
            params_df = pd.read_csv(params_file)
            thresh_ind = params_df["threshold filter"].values[0]
            radio_button_gp.active = int(thresh_ind)
            if thresh_ind == 6:
                offset_spinner.value = int(params_df["local offset"].values[0])
                block_spinner.value = int(params_df["local block size"].values[0])
            contour_rng_slider.value = tuple([int(params_df["contour min"].values[0]),int(params_df["contour max"].values[0])])
            find_contours()
        else:
            print("ROI files not found! Check save directory or export ROIs.{:<100}".format(' '),end="\r")

    # use FFMPEG to crop out regions from original mp4 and save to file

    def create_ROI_movies():
        if source.data['input_dir'][0] == '':
            print('No image loaded! Load image first.{:<100}'.format(' '),end="\r")
        else:
            check_ROI_files()
            side = 100 # for square ROIs, replace first two crop parameters with side & uncomment
            padding = 20
            roi_coords_file = os.path.join(source.data['output_dir'][0],'ROI_coords.csv')
            if source.data["num_contours"]==[0]:
                print("No contours found! Find contours first.{:<100}".format(' '),end="\r")
            elif not os.path.exists(roi_coords_file):
                print("ROI file does not exist! Check save directory or export ROIs.{:<100}".format(' '),end="\r")
            else:
                print('Creating Movies...{:<100}'.format(' '),end="\r")
                pbar = tqdm(total=source.data["num_contours"][0])
                for i in range(source.data["num_contours"][0]):
                    roi_coords = np.array(source.data["roi_coords"][0])
                    inPath = os.path.join(source.data['input_dir'][0],source.data['img_name'][0])
                    #out_fname = source.data['filename'][0].split('.')[0] +'_ROI'+str(i+1)+'.mp4'
                    out_fname = 'ROI'+str(i+1)+'.mp4'
                    outPath = os.path.join(source.data['output_dir'][0],out_fname)
                    #command = f"ffmpeg -i \'{(inPath)}\' -vf \"crop={(roi_coords[i,0]+padding*2)}:{(roi_coords[i,1]+padding*2)}:{(roi_coords[i,2]-padding)}:{(roi_coords[i,3]+padding)}\" -y \'{(outPath)}\'"
                    command = f"ffmpeg -i \'{(inPath)}\' -vf \"crop={side}:{side}:{(roi_coords[i,2]-padding)}:{(roi_coords[i,3]-padding)}\" -y \'{(outPath)}\'"
                    overwrite = 'no'
                    if os.path.exists(outPath):
                        overwrite = overwrite_file()
                    if overwrite == 'yes' or not os.path.exists(outPath):
                        saved = subprocess.check_call(command,shell=True)
                        if saved != 0:
                            print('An error occurred while creating movies! Check terminal window.{:<100}'.format(' '),end="\r")
                    pbar.update()

    # change the display range on images from slider values

    def update_image():
        cmap.low = display_range_slider.value[0]
        cmap.high = display_range_slider.value[1]

    # create statistics files for each mp4 region specific file

    def process_ROIs():
        if source.data['input_dir'][0] == '':
            print('No image loaded! Load image first.{:<100}'.format(' '),end="\r")
        else:
            check_ROI_files()
            hdr = 'roi,time,area,mean,variance,min,max,median,skewness,kurtosis,rawDensity,COMx,COMy'
            cols = hdr.split(',')
            all_stats = np.zeros((0,13))
            n_cnt = source.data["num_contours"][0]
            if n_cnt == 0:
                print("No contours found! Find contours first.{:<100}".format(' '),end="\r")
            for i in range(n_cnt): 
                #in_fname = source.data['filename'][0].split('.')[0] +'_ROI'+str(i+1)+'.mp4'
                in_fname = 'ROI'+str(i+1)+'.mp4'
                inPath = os.path.join(source.data['output_dir'][0],in_fname)
                #out_fname = source.data['filename'][0].split('.')[0] +'_ROI'+str(i+1)+'_stats.csv'
                out_fname = 'stats_ROI'+str(i+1)+'.csv'
                outPath = os.path.join(source.data['output_dir'][0],out_fname)
                if not os.path.exists(inPath):
                    print('ROI movie not found! Create ROI movie first.{:<100}'.format(' '),end="\r")
                    break
                vidcap = cv2.VideoCapture(inPath)
                last_frame = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
                if i==0:
                    pbar = tqdm(total=last_frame*n_cnt)
                success,frame = vidcap.read()
                img_tmp,_,__ = cv2.split(frame)
                h,w = np.shape(img_tmp)
                img = np.zeros((last_frame,h,w))
                img_stats = np.zeros((last_frame,13))
                stats = describe(img_tmp,axis=None)
                median = np.median(img_tmp)
                density = np.sum(img_tmp)
                cx, cy = center_of_mass(img_tmp)
                img_stats[0,0:13] = [i,0,stats.nobs,stats.mean,stats.variance,
                        stats.minmax[0],stats.minmax[1],median,stats.skewness,
                        stats.kurtosis,density,cx,cy]
                img[0,:,:] = np.flipud(img_tmp)
                pbar.update()
                overwrite = 'no'
                if os.path.exists(outPath):
                    overwrite = overwrite_file()
                    if overwrite=='no':
                        pbar.update(last_frame-1)
                if overwrite == 'yes' or not os.path.exists(outPath):
                    for j in range(1,last_frame):
                        vidcap.set(1, j)
                        success,frame = vidcap.read()
                        img_tmp,_,__ = cv2.split(frame)
                        stats = describe(img_tmp,axis=None)
                        t = j*5/60
                        density = np.sum(img_tmp)
                        cx, cy = center_of_mass(img_tmp)
                        median = np.median(img_tmp)
                        img_stats[j,0:13] = [i,t,stats.nobs,stats.mean,stats.variance,
                                stats.minmax[0],stats.minmax[1],median,stats.skewness,
                                stats.kurtosis,density,cx,cy]
                        img[j,:,:] = np.flipud(img_tmp)
                        pbar.update(1)
                    all_stats = np.append(all_stats,img_stats,axis=0)
                    np.savetxt(outPath,img_stats,delimiter=',',header=hdr,comments='')
                if i==(n_cnt-1):
                    df = pd.DataFrame(all_stats,columns=cols)
                    group = df.groupby('roi')
                    for i in range(len(group)):
                        sources_stats[i] = ColumnDataSource(group.get_group(i))

    # load statistics CSVs and first ROI mp4 files and display in plots

    def load_ROI_files():
        if source.data['input_dir'][0] == '':
            print('No image loaded! Load image first.{:<100}'.format(' '),end="\r")
        else:
            check_ROI_files()
            n_cnt = source.data["num_contours"][0]
            basepath = os.path.join(source.data["output_dir"][0],'stats')
            all_files = [basepath+'_ROI'+str(i+1)+'.csv' for i in range(n_cnt)]
            files_exist = [os.path.exists(f) for f in all_files]
            if all(files_exist) and n_cnt != 0:
                df = pd.concat((pd.read_csv(f) for f in all_files), ignore_index=True)
                group = df.groupby('roi')
                OPTIONS = []
                LABELS = []
                pbar = tqdm(total=len(stats)*len(group))
                j=0
                colors_ordered = list(Category20[20])
                idx_reorder = np.append(np.linspace(0,18,10),np.linspace(1,19,10))
                idx = idx_reorder.astype(int)
                colors = [colors_ordered[i] for i in idx]
                for roi, df_roi in group:
                    sources_stats[roi] = ColumnDataSource(df_roi)
                    OPTIONS.append([str(int(roi)+1),'ROI '+(str(int(roi)+1))])
                    LABELS.append('ROI '+str(int(roi)+1))
                    color = colors[j]
                    j+=1
                    if j>=20:
                        j=0
                    for i in range(3,len(df.columns)):
                        name = 'ROI '+str(int(roi)+1)
                        plot_check = p_stats[i-3].select_one({'name':name})
                        if not plot_check:
                            p_stats[i-3].line(x='time',y=str(df.columns[i]),source=sources_stats[roi],
                                name=name,visible=False,line_color=color)
                            p_stats[i-3].xaxis.axis_label = "Time [h]"
                            p_stats[i-3].yaxis.axis_label = str(df.columns[i])
                            p_stats[i-3].add_tools(HoverTool(tooltips=TOOLTIPS))
                            p_stats[i-3].toolbar_location = "left"
                        pbar.update(1)
                ROI_multi_select.options = OPTIONS 
                ROI_multi_select.value = ["1"]
                ROI_movie_radio_group.labels = LABELS
                ROI_movie_radio_group.active = 0
            else:
                print('Not enough files! Check save directory or calculate new stats.{:<100}'.format(' '),end="\r")

    # show/hide curves from selected/deselected labels for ROIs in statistics plots

    def update_ROI_plots():
        n_cnt = source.data["num_contours"][0]
        pbar = tqdm(total=len(stats)*n_cnt)
        for j in range(n_cnt):
            for i in range(len(stats)):
                name = 'ROI '+str(int(j)+1)
                glyph = p_stats[i].select_one({'name': name})
                if str(j+1) in ROI_multi_select.value:
                    glyph.visible = True
                else:
                    glyph.visible = False
                pbar.update(1)

    # load and display the selected ROI's mp4

    def load_ROI_movie():
        idx = ROI_movie_radio_group.active
        in_fname = 'ROI'+str(idx+1)+'.mp4'
        inPath = os.path.join(source.data['output_dir'][0],in_fname)
        if not os.path.exists(inPath):
            print('ROI movie not found! Check save directory or create ROI movie.',end="\r")
        else:
            old_plot = p_ROI.select_one({'name': sourceROI.data['img_name'][0]})
            if old_plot:
                p_ROI.renderers.remove(old_plot)
            vidcap = cv2.VideoCapture(inPath)
            last_frame = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
            ROI_movie_slider.end = (last_frame-1)*5/60
            ROI_movie_slider.value = 0
            vidcap.set(1, 0)
            success,frame = vidcap.read()
            img_tmp,_,__ = cv2.split(frame)
            h,w = np.shape(img_tmp)
            img = np.flipud(img_tmp)
            name = 'ROI'+str(idx+1)
            sourceROI.data = dict(image=[img],x=[0], y=[0], dw=[w], dh=[h],
                img_name=[name])
            p_ROI.image(source=sourceROI, image='image', x='x', y='y', 
               dw='dw', dh='dh', color_mapper=cmap, name='img_name')

    # change the displayed frame from slider movement

    def update_ROI_movie():
        frame_idx = round(ROI_movie_slider.value*60/5)
        in_fname = sourceROI.data['img_name'][0]+'.mp4'
        inPath = os.path.join(source.data['output_dir'][0],in_fname)
        vidcap = cv2.VideoCapture(inPath)
        vidcap.set(1, frame_idx)
        success,frame = vidcap.read()
        img_tmp,_,__ = cv2.split(frame)
        img = np.flipud(img_tmp)
        sourceROI.data['image'] = [img]

    # the following 2 functions are used to animate the mp4

    def update_ROI_slider():
        time = ROI_movie_slider.value + 5/60
        end = ROI_movie_slider.end
        if time > end:
            animate_ROI_movie()
        else:
            ROI_movie_slider.value = time
        return callback_id

    def animate_ROI_movie():
        global callback_id
        if ROI_movie_play_button.label == '► Play':
            ROI_movie_play_button.label = '❚❚ Pause'
            callback_id = curdoc().add_periodic_callback(update_ROI_slider, 10)
        else:
            ROI_movie_play_button.label = '► Play'
            curdoc().remove_periodic_callback(callback_id)
        return callback_id

### Application Content ###

    # main plot for segmentation and contour finding

    cmap = LinearColorMapper(palette="Greys256", low=0, high=255)
    TOOLS = "pan,wheel_zoom,box_zoom,reset,save,box_select,lasso_select"
    IMG_TOOLTIPS = [('name', "@img_name"),("x", "$x"),("y", "$y"),("value", "@image")]

    source = ColumnDataSource(data=dict(image=[0],bin_img=[0],image_orig=[0],
            x=[0], y=[0], dw=[0], dh=[0], num_contours=[0], roi_coords=[0],
            input_dir=[''],output_dir=[''],img_name=['']))
    source_label = ColumnDataSource(data=dict(x=[0], y=[0], label=['']))
    source_contours = ColumnDataSource(data=dict(xs=[0], ys=[0]))

    roi_labels = LabelSet(x='x', y='y', text='label',source=source_label, 
        level='annotation',text_color='white',text_font_size='12pt')

    # create a new plot and add a renderer
    p = figure(tools=TOOLS, toolbar_location=("right"))
    p.add_layout(roi_labels)
    p.x_range.range_padding = p.y_range.range_padding = 0

    # turn off gridlines
    p.xgrid.grid_line_color = None
    p.ygrid.grid_line_color = None
    p.axis.visible = False


    # ROI plots 

    sourceROI = ColumnDataSource(data=dict(image=[0],
            x=[0], y=[0], dw=[0], dh=[0], img_name=[0]))
    sources_stats = {}

    TOOLTIPS = [('name','$name'),('time', '@time'),('stat', "$y")]
    stats = np.array(['mean','var','min','max','median','skew','kurt','rawDensity','COMx','COMy'])
    p_stats = []
    tabs = []
    for i in range(len(stats)):
        p_stats.append(figure(tools=TOOLS, plot_height=300, plot_width=600))
        p_stats[i].x_range.range_padding = p_stats[i].y_range.range_padding = 0
        tabs.append(Panel(child=p_stats[i], title=stats[i]))

    # create a new plot and add a renderer
    p_ROI = figure(tools=TOOLS, toolbar_location=("right"), plot_height=300, plot_width=300)
    p_ROI.x_range.range_padding = p_ROI.y_range.range_padding = 0  

    # turn off gridlines
    p_ROI.xgrid.grid_line_color = p_ROI.ygrid.grid_line_color = None
    p_ROI.axis.visible = False


    # Widgets - Buttons, Sliders, Text, Etc.

    intro = Div(text="""<h2>Droplet Recognition and Analysis with Bokeh</h2> 
        This application is designed to help segment a grayscale image into 
        regions of interest (ROIs) and perform analysis on those regions.<br>
        <h4>How to Use This Application:</h4>
        <ol>
        <li>Load in a grayscale mp4 file and choose a save directory.</li>
        <li>Apply various filters for thresholding. Use <b>Close</b>, <b>Dilate</b> 
        and <b>Erode</b> buttons to adjust each binary image further.</li>
        <li>Use <b>Find Contours</b> button to search the image for closed shape. 
        The <b>Contour Size Range</b> slider will change size of the perimeter to
        be identified. You can apply new thresholds and repeat until satisfied with
        the region selection. Total regions detected is displayed next to
        the button.</li>
        <li>When satisfied, use <b>Export ROIs</b> to write ROI locations and 
        contour finding parameters to file.</li>
        <li><b>Create ROI Movies</b> to write mp4s of the selected regions.</li>
        <li>Use <b>Calculate ROI Stats</b> to perform calculations on the 
        newly created mp4 files.</li>
        <li>Finally, use <b>Load ROI Files</b> to load in the data that you just
        created and view the plots. The statistics plots can be overlaid by 
        selecting multiple labels. Individual ROI mp4s can be animated or you can
        use the slider to move through the frames.</li>
        </ol>
        Note: messages and progress bars are displayed below the GUI.""",
        style={'font-size':'10pt'},width=1000)

    file_button = Button(label="Choose File",button_type="primary")
    file_button.on_click(update_filename)
    inFile = PreText(text='Input File:\n'+source.data["img_name"][0], background=(255,255,255,0.5), width=500)

    filter_LABELS = ["Original","OTSU", "Isodata", "Mean", "Li","Yen","Local"]
    radio_button_gp = RadioButtonGroup(labels=filter_LABELS, active=0, width=600)
    radio_button_gp.on_change('active', lambda attr, old, new: apply_filter())
    
    offset_spinner = Spinner(low=0, high=500, value=1, step=1, width=100, title="Local: Offset",
                            background=(255,255,255,0.5))
    offset_spinner.on_change('value', lambda attr, old, new: apply_filter())
    block_spinner = Spinner(low=1, high=101, value=25, step=2, width=100, title="Local: Block Size",
                           background=(255,255,255,0.5))
    block_spinner.on_change('value', lambda attr, old, new: apply_filter())
    
    closing_button = Button(label="Close",button_type="default", width=100)
    closing_button.on_click(close_img)
    dilation_button = Button(label="Dilate",button_type="default", width=100)
    dilation_button.on_click(dilate_img)
    erosion_button = Button(label="Erode",button_type="default", width=100)
    erosion_button.on_click(erode_img)

    contour_rng_slider = RangeSlider(start=10, end=500, value=(200,350), step=1, width=300, 
            title="Contour Size Range", background=(255,255,255,0.5), bar_color='gray')
    contour_button = Button(label="Find Contours", button_type="success")
    contour_button.on_click(find_contours)
    contours_found = PreText(text='Droplets Detected: '+str(source.data["num_contours"][0]), background=(255,255,255,0.5))
    
    exportROIs_button = Button(label="Export ROIs", button_type="success", width=200)
    exportROIs_button.on_click(export_ROIs)    

    changeDir_button = Button(label="Change Directory",button_type="primary", width=150)
    changeDir_button.on_click(change_directory)
    outDir = PreText(text='Save Directory:\n'+source.data["output_dir"][0], background=(255,255,255,0.5), width=500)

    create_ROIs_button = Button(label="Create ROI Movies",button_type="success", width=200)
    create_ROIs_button.on_click(create_ROI_movies)

    process_ROIs_button = Button(label="Calculate ROI Stats",button_type="success")
    process_ROIs_button.on_click(process_ROIs)

    display_rng_text = figure(title="Display Range", title_location="left", 
                        width=40, height=300, toolbar_location=None, min_border=0, 
                        outline_line_color=None)
    display_rng_text.title.align="center"
    display_rng_text.title.text_font_size = '10pt'
    display_rng_text.x_range.range_padding = display_rng_text.y_range.range_padding = 0

    display_range_slider = RangeSlider(start=0, end=255, value=(0,255), step=1, 
        orientation='vertical', direction='rtl', 
        bar_color='gray', width=40, height=300, tooltips=True)
    display_range_slider.on_change('value', lambda attr, old, new: update_image())

    load_ROIfiles_button = Button(label="Load ROI Files",button_type="primary")
    load_ROIfiles_button.on_click(load_ROI_files)

    ROI_multi_select = MultiSelect(value=[], width=100, height=300)
    ROI_multi_select.on_change('value', lambda attr, old, new: update_ROI_plots())

    ROI_movie_radio_group = RadioGroup(labels=[],width=60)
    ROI_movie_radio_group.on_change('active', lambda attr, old, new: load_ROI_movie())
    ROI_movie_slider = Slider(start=0,end=100,value=0,step=5/60,title="Time [h]", width=280)
    ROI_movie_slider.on_change('value', lambda attr, old, new: update_ROI_movie())

    callback_id = None

    ROI_movie_play_button = Button(label='► Play',width=50)
    ROI_movie_play_button.on_click(animate_ROI_movie)

# initialize some data without having to choose file
    # fname = os.path.join(os.getcwd(),'data','Droplets.mp4')
    # load_data(filename=fname)


### Layout & Initialize application ###

    ROI_layout = layout([
        [ROI_movie_radio_group, p_ROI],
        [ROI_movie_slider,ROI_movie_play_button]
        ])

    app = layout(children=[
        [intro],
        [file_button,inFile],
        [radio_button_gp, offset_spinner, block_spinner],
        [closing_button, dilation_button, erosion_button],
        [contour_rng_slider, contour_button, contours_found],
        [exportROIs_button, outDir, changeDir_button],
        [create_ROIs_button, process_ROIs_button],
        [display_rng_text, display_range_slider, p],
        [load_ROIfiles_button],
        [ROI_layout, ROI_multi_select, Tabs(tabs=tabs)]
    ])
    
    doc.add_root(app)