Ejemplo n.º 1
0
    def create_displayed_values_toggle(self):
        """
        Create the range sliders' visibility toggle button.

        Returns
        -------
        Toggle
            A Toggle type button instance to control range sliders' visibility.
        """

        displayed_values_toggle = Toggle(label="Displayed Values")
        displayed_values_toggle.on_click(self.toggle_range_sliders_visibility)
        return displayed_values_toggle
Ejemplo n.º 2
0
    def create_index_sliders_toggle(self) -> Toggle:
        """
        Create the index sliders' visibility toggle button.

        Returns
        -------
        Toggle
            A Toggle type button instance to control index sliders' visibility.
        """

        sliders_toggle = Toggle(label="Plane Indices", active=False)
        sliders_toggle.on_click(self.toggle_index_sliders_visibility)
        return sliders_toggle
Ejemplo n.º 3
0
class BasicOption:
    """ Basic option class. If you need to add further attributes to your
    options, create your class and inherit from this. """
    number: int
    name: str
    gui_name: str
    selected: bool
    incompatibilities: List[str]

    def __init__(self,
                 number: int,
                 name: str,
                 gui_name: str = "",
                 selected: bool = False,
                 incompatibilities: List[str] = []):
        self.number = number
        self.name = name
        self.gui_name = gui_name if gui_name else name
        self.selected = selected
        self.incompatibilities = incompatibilities
        self.incompatibility_count = 0
        self.button = Toggle(label=self.gui_name,
                             width=200,
                             button_type="primary")
        self.button.on_click(self.button_function)

    def button_function(self, state):
        """ Disables incompatible options. """
        if state:  # styling
            self.button.button_type = "success"
        else:
            self.button.button_type = "primary"
        for incomp_opt in self.incompatibilities:  # incompatibility logic
            if state:
                incomp_opt.incompatibility_count += 1
                incomp_opt.button.disabled = True
            else:
                incomp_opt.incompatibility_count -= 1
                if incomp_opt.incompatibility_count == 0:
                    incomp_opt.button.disabled = False
def generate_gui(tsne, cut_extracellular_data, all_extra_spike_times, time_axis, cluster_info_file,
                 use_existing_cluster, autocor_bin_number, sampling_freq, prb_file=None, k4=False, verbose=False):

    if k4:
        tsne_figure_size = [1000, 800]
        tsne_min_border_left = 50
        spike_figure_size = [500, 500]
        hist_figure_size = [500, 500]
        heatmap_plot_size = [200, 800]
        clusters_table_size = [400, 300]
        layout_size = [1500, 1400]
        slider_size = [300, 100]
        user_info_size = [700, 80]
    else:
        tsne_figure_size = [850, 600]
        tsne_min_border_left = 10
        spike_figure_size = [450, 300]
        hist_figure_size = [450, 300]
        heatmap_plot_size = [200, 800]
        clusters_table_size = [400, 400]
        layout_size = [1200, 800]
        slider_size = [270, 80]
        user_info_size = [450, 80]
    # Plots ------------------------------
    # scatter plot
    global non_selected_points_alpha
    global selected_points_size
    global non_selected_points_size
    global update_old_selected_switch
    global previously_selected_spike_indices

    tsne_fig_tools = "pan,wheel_zoom,box_zoom,box_select,lasso_select,tap,resize,reset,save"
    tsne_figure = figure(tools=tsne_fig_tools, plot_width=tsne_figure_size[0], plot_height=tsne_figure_size[1],
                         title='T-sne', min_border=10, min_border_left=tsne_min_border_left, webgl=True)

    tsne_source = ColumnDataSource({'tsne-x': tsne[0], 'tsne-y': tsne[1]})

    tsne_selected_points_glyph = Circle(x='tsne-x', y='tsne-y', size=selected_points_size,
                                        line_alpha=0, fill_alpha=1, fill_color='red')
    tsne_nonselected_points_glyph = Circle(x='tsne-x', y='tsne-y', size=non_selected_points_size,
                                           line_alpha=0, fill_alpha=non_selected_points_alpha, fill_color='blue')
    tsne_invisible_points_glyph = Circle(x='tsne-x', y='tsne-y', size=selected_points_size, line_alpha=0, fill_alpha=0)

    tsne_nonselected_glyph_renderer = tsne_figure.add_glyph(tsne_source, tsne_nonselected_points_glyph,
                                                            selection_glyph=tsne_invisible_points_glyph,
                                                            nonselection_glyph=tsne_nonselected_points_glyph,
                                                            name='tsne_nonselected_glyph_renderer')
        # note: the invisible glyph is required to be able to change the size of the selected points, since the
        # use of selection_glyph is usefull only for colors and alphas
    tsne_invinsible_glyph_renderer = tsne_figure.add_glyph(tsne_source, tsne_invisible_points_glyph,
                                                           selection_glyph=tsne_selected_points_glyph,
                                                           nonselection_glyph=tsne_invisible_points_glyph,
                                                           name='tsne_invinsible_glyph_renderer')


    tsne_figure.select(BoxSelectTool).select_every_mousemove = False
    tsne_figure.select(LassoSelectTool).select_every_mousemove = False


    def on_tsne_data_update(attr, old, new):
        global previously_selected_spike_indices
        global currently_selected_spike_indices
        global non_selected_points_alpha
        global non_selected_points_size
        global selected_points_size
        global checkbox_find_clusters_of_selected_points

        previously_selected_spike_indices = np.array(old['1d']['indices'])
        currently_selected_spike_indices = np.array(new['1d']['indices'])
        num_of_selected_spikes = len(currently_selected_spike_indices)

        if num_of_selected_spikes > 0:
            if verbose:
                print('Num of selected spikes = ' + str(num_of_selected_spikes))

            # update t-sne plot
            tsne_invisible_points_glyph.size = selected_points_size
            tsne_nonselected_points_glyph.size = non_selected_points_size
            tsne_nonselected_points_glyph.fill_alpha = non_selected_points_alpha

            # update spike plot
            avg_x = np.mean(cut_extracellular_data[:, :, currently_selected_spike_indices], axis=2)
            spike_mline_plot.data_source.data['ys'] = avg_x.tolist()
            print('Finished avg spike plot')

            # update autocorelogram
            diffs, norm = crosscorrelate_spike_trains(all_extra_spike_times[currently_selected_spike_indices].astype(np.int64),
                                                      all_extra_spike_times[currently_selected_spike_indices].astype(np.int64), lag=1500)
            hist, edges = np.histogram(diffs, bins=autocor_bin_number)
            hist_plot.data_source.data["top"] = hist
            hist_plot.data_source.data["left"] = edges[:-1] / sampling_freq
            hist_plot.data_source.data["right"] = edges[1:] / sampling_freq
            print('finished autocorelogram')

            # update heatmap
            if prb_file is not None:
                print('Doing heatmap')
                data = cut_extracellular_data[:, :, currently_selected_spike_indices]
                final_image, (x_size, y_size) = spike_heatmap.create_heatmap(data, prb_file, rotate_90=True,
                                                                             flip_ud=True, flip_lr=False)
                new_image_data = dict(image=[final_image], x=[0], y=[0], dw=[x_size], dh=[y_size])
                heatmap_data_source.data.update(new_image_data)
                print('Finished heatmap')

    tsne_source.on_change('selected', on_tsne_data_update)

    # spike plot
    spike_fig_tools = 'pan,wheel_zoom,box_zoom,reset,save'
    spike_figure = figure(toolbar_location='below', plot_width=spike_figure_size[0], plot_height=spike_figure_size[1],
                          tools=spike_fig_tools, title='Spike average', min_border=10, webgl=True, toolbar_sticky=False)

    num_of_channels = cut_extracellular_data.shape[0]
    num_of_time_points = cut_extracellular_data.shape[1]
    xs = np.repeat(np.expand_dims(time_axis, axis=0), repeats=num_of_channels, axis=0).tolist()
    ys = np.ones((num_of_channels, num_of_time_points)).tolist()
    spike_mline_plot = spike_figure.multi_line(xs=xs, ys=ys)

    # autocorelogram plot
    hist, edges = np.histogram([], bins=autocor_bin_number)
    hist_fig_tools = 'pan,wheel_zoom,box_zoom,save,reset'

    hist_figure = figure(toolbar_location='below', plot_width=hist_figure_size[0], plot_height=hist_figure_size[1],
                         tools=hist_fig_tools, title='Autocorrelogram', min_border=10, webgl=True, toolbar_sticky=False)
    hist_plot = hist_figure.quad(bottom=0, left=edges[:-1], right=edges[1:], top=hist, color="#3A5785", alpha=0.5,
                                 line_color="#3A5785")
    # heatmap plot
    heatmap_plot = figure(toolbar_location='right', plot_width=1, plot_height=heatmap_plot_size[1],
                          x_range=(0, 1), y_range=(0, 1), title='Probe heatmap',
                          toolbar_sticky=False)
    if prb_file is not None:
        data = np.zeros(cut_extracellular_data.shape)
        final_image, (x_size, y_size) = spike_heatmap.create_heatmap(data, prb_file, rotate_90=True,
                                                                     flip_ud=True, flip_lr=False)
        final_image[:, :, ] = 4294967295  # The int32 for the int8 255 (white)
        plot_width = max(heatmap_plot_size[0], int(heatmap_plot_size[1] * y_size / x_size))
        heatmap_plot = figure(toolbar_location='right', plot_width=plot_width, plot_height=heatmap_plot_size[1],
                              x_range=(0, x_size), y_range=(0, y_size), title='Probe heatmap',
                              toolbar_sticky=False)

        heatmap_data_source = ColumnDataSource(data=dict(image=[final_image], x=[0], y=[0], dw=[x_size], dh=[y_size]))
        heatmap_renderer = heatmap_plot.image_rgba(source=heatmap_data_source, image='image', x='x', y='y',
                                                   dw='dw', dh='dh', dilate=False)
        heatmap_plot.axis.visible = None
        heatmap_plot.xgrid.grid_line_color = None
        heatmap_plot.ygrid.grid_line_color = None
    # ---------------------------------------
    # --------------- CONTROLS --------------
    # Texts and Tables
    # the clusters DataTable
    if use_existing_cluster:
        cluster_info = load_cluster_info(cluster_info_file)
    else:
        cluster_info = create_new_cluster_info_file(cluster_info_file, len(tsne))
    cluster_info_data_source = ColumnDataSource(cluster_info)
    clusters_columns = [TableColumn(field='Cluster', title='Clusters'),
                        TableColumn(field='Num_of_Spikes', title='Number of Spikes')]
    clusters_table = DataTable(source=cluster_info_data_source, columns=clusters_columns, selectable=True,
                               editable=False, width=clusters_table_size[0], height=clusters_table_size[1],
                               scroll_to_selection=True)

    def on_select_cluster_info_table(attr, old, new):
        global selected_cluster_names
        cluster_info = load_cluster_info(cluster_info_file)
        indices = list(chain.from_iterable(cluster_info.iloc[new['1d']['indices']].Spike_Indices.tolist()))
        selected_cluster_names = list(cluster_info.index[new['1d']['indices']])
        old = new = tsne_source.selected
        tsne_source.selected['1d']['indices'] = indices
        tsne_source.trigger('selected', old, new)
        user_info_edit.value = 'Selected clusters = ' + ', '.join(selected_cluster_names)

    cluster_info_data_source.on_change('selected', on_select_cluster_info_table)

    def update_data_table():
        cluster_info_data_source = ColumnDataSource(load_cluster_info(cluster_info_file))
        cluster_info_data_source.on_change('selected', on_select_cluster_info_table)
        clusters_table.source = cluster_info_data_source
        options = list(cluster_info_data_source.data['Cluster'])
        options.insert(0, 'No cluster selected')
        select_cluster_to_move_points_to.options = options

    # cluster TextBox that adds cluster to the DataTable
    new_cluster_name_edit = TextInput(value='give the new cluster a name',
                                      title='Put selected points into a new cluster')

    def on_text_edit_new_cluster_name(attr, old, new):
        global currently_selected_spike_indices
        global clusters_of_all_spikes

        new_cluster_name = new_cluster_name_edit.value

        spike_indices_to_delete_from_existing_clusters = {}
        for spike_index in currently_selected_spike_indices:
            if clusters_of_all_spikes[spike_index] != -1:
                cluster_index = clusters_of_all_spikes[spike_index]
                if cluster_index not in spike_indices_to_delete_from_existing_clusters:
                    spike_indices_to_delete_from_existing_clusters[cluster_index] = [spike_index]
                else:
                    spike_indices_to_delete_from_existing_clusters[cluster_index].append(spike_index)
        cluster_info = load_cluster_info(cluster_info_file)
        for cluster_index in spike_indices_to_delete_from_existing_clusters.keys():
            cluster_name = cluster_info.iloc[cluster_index].name
            remove_spikes_from_cluster(cluster_info_file, cluster_name,
                                       spike_indices_to_delete_from_existing_clusters[cluster_index], unassign=False)

        add_cluster_to_cluster_info(cluster_info_file, new_cluster_name, currently_selected_spike_indices)

        update_data_table()

    new_cluster_name_edit.on_change('value', on_text_edit_new_cluster_name)

    # user information Text
    user_info_edit = TextInput(value='', title='User information',
                               width=user_info_size[0], height=user_info_size[1])

    # Buttons ------------------------
    # show all clusters Button
    button_show_all_clusters = Toggle(label='Show all clusters', button_type='primary')

    def on_button_show_all_clusters(state, *args):
        global tsne_clusters_scatter_plot

        if state:
            cluster_info = load_cluster_info(cluster_info_file)
            num_of_clusters = cluster_info.shape[0]
            indices_list_of_lists = cluster_info['Spike_Indices'].tolist()
            indices = [item for sublist in indices_list_of_lists for item in sublist]
            cluster_indices = np.arange(num_of_clusters)

            if verbose:
                print('Showing all clusters in colors... wait for it...')

            colors = []
            for c in cluster_indices:
                r = np.random.random(size=1) * 255
                g = np.random.random(size=1) * 255
                for i in np.arange(len(indices_list_of_lists[c])):
                    colors.append("#%02x%02x%02x" % (int(r), int(g), 50))

            first_time = True
            for renderer in tsne_figure.renderers:
                if renderer.name == 'tsne_all_clusters_glyph_renderer':
                    renderer.data_source.data['fill_color'] = renderer.data_source.data['line_color'] = colors
                    renderer.glyph.fill_color = 'fill_color'
                    renderer.glyph.line_color = 'line_color'
                    first_time = False
                    break
            if first_time:
                tsne_clusters_scatter_plot = tsne_figure.scatter(tsne[0][indices], tsne[1][indices], size=1,
                                                                 color=colors, alpha=1,
                                                                 name='tsne_all_clusters_glyph_renderer')
            tsne_clusters_scatter_plot.visible = True
            button_show_all_clusters.label = 'Hide all clusters'
        else:
            if verbose:
                print('Hiding clusters')
            button_show_all_clusters.update()
            tsne_clusters_scatter_plot.visible = False
            button_show_all_clusters.label = 'Show all clusters'

    button_show_all_clusters.on_click(on_button_show_all_clusters)


    # select the clusters that the selected points belong to Button
    # (that will then drive the selection of these spikes on t-sne through the update of the clusters_table source)
    button_show_clusters_of_selected_points = Button(label='Show clusters of selected points')

    def on_button_show_clusters_change():
        print('Hello')
        global clusters_of_all_spikes
        currently_selected_spike_indices = tsne_source.selected['1d']['indices']
        cluster_info = load_cluster_info(cluster_info_file)
        clusters_selected = []
        new_indices_to_select = []
        update_data_table()
        for spike_index in currently_selected_spike_indices:
            if clusters_of_all_spikes[spike_index] not in clusters_selected:
                clusters_selected.append(clusters_of_all_spikes[spike_index])
                indices_in_cluster = cluster_info.iloc[clusters_of_all_spikes[spike_index]].Spike_Indices
                new_indices_to_select.append(indices_in_cluster)
        if len(new_indices_to_select) > 0:
            old = clusters_table.source.selected
            clusters_table.source.selected['1d']['indices'] = clusters_selected
            new = clusters_table.source.selected
            clusters_table.source.trigger('selected', old, new)
            for c in np.arange(len(clusters_selected)):
                clusters_selected[c] = cluster_info.index[clusters_selected[c]]


    button_show_clusters_of_selected_points.on_click(on_button_show_clusters_change)

    # merge clusters Button
    button_merge_clusters_of_selected_points = Button(label='Merge clusters of selected points')

    def on_button_merge_clusters_change():
        global clusters_of_all_spikes
        currently_selected_spike_indices = tsne_source.selected['1d']['indices']
        cluster_info = load_cluster_info(cluster_info_file)
        clusters_selected = []
        for spike_index in currently_selected_spike_indices:
            if clusters_of_all_spikes[spike_index] not in clusters_selected:
                clusters_selected.append(clusters_of_all_spikes[spike_index])
        if len(clusters_selected) > 0:
            clusters_selected = np.sort(clusters_selected)
            clusters_selected_names = []
            for cluster_index in clusters_selected:
                clusters_selected_names.append(cluster_info.iloc[cluster_index].name)
            cluster_name = clusters_selected_names[0]
            add_cluster_to_cluster_info(cluster_info_file, cluster_name, currently_selected_spike_indices)
            i = 0
            for c in np.arange(1, len(clusters_selected)):
                cluster_info = remove_cluster_from_cluster_info(cluster_info_file,
                                                                cluster_info.iloc[clusters_selected[c] - i].name,
                                                                unassign=False)
                i = i + 1 # Every time you remove a cluster the original index of the remaining clusters drops by one

            update_data_table()
            user_info_edit.value = 'Clusters '+ ', '.join(clusters_selected_names) + ' merged to cluster ' + cluster_name

    button_merge_clusters_of_selected_points.on_click(on_button_merge_clusters_change)

    # delete cluster Button
    button_delete_cluster = Button(label='Delete selected cluster(s)')

    def on_button_delete_cluster():
        global selected_cluster_names
        for cluster_name in selected_cluster_names:
            remove_cluster_from_cluster_info(cluster_info_file, cluster_name)
        user_info_edit.value = 'Deleted clusters: ' + ', '.join(selected_cluster_names)
        update_data_table()

    button_delete_cluster.on_click(on_button_delete_cluster)

    # select cluster to move selected points to Select
    select_cluster_to_move_points_to = Select(title="Assign selected points to cluster:", value="No cluster selected")

    options = list(cluster_info_data_source.data['Cluster'])
    options.insert(0, 'No cluster selected')
    select_cluster_to_move_points_to.options = options


    def move_selected_points_to_cluster(attr, old, new):
        global currently_selected_spike_indices
        if len(currently_selected_spike_indices) > 0 and new is not 'No cluster selected':
            remove_spikes_from_all_clusters(cluster_info_file, currently_selected_spike_indices)
            add_spikes_to_cluster(cluster_info_file, new, currently_selected_spike_indices)
            update_data_table()
            select_cluster_to_move_points_to.value = 'No cluster selected'
            user_info_edit.value = 'Selected clusters = ' + new

    select_cluster_to_move_points_to.on_change('value', move_selected_points_to_cluster)


    # undo selection button
    undo_selected_points_button = Button(label='Undo last selection')

    def on_button_undo_selection():
        global previously_selected_spike_indices
        tsne_source.selected['1d']['indices'] = previously_selected_spike_indices
        old = new = tsne_source.selected
        tsne_source.trigger('selected', old, new)

    undo_selected_points_button.on_click(on_button_undo_selection)

    # Sliders -------------------
    # use the fake data trick to call the callback only when the mouse is released (mouseup only works for CustomJS)

    # change visibility of non selected points Slider
    slider_non_selected_visibility = Slider(start=0, end=1, value=0.2, step=.02, callback_policy='mouseup',
                                            title='Alpha of not selected points',
                                            width=slider_size[0], height=slider_size[1])

    def on_slider_change_non_selected_visibility(attrname, old, new):
        global non_selected_points_alpha
        if len(source_fake_nsv.data['value']) > 0:
            non_selected_points_alpha = source_fake_nsv.data['value'][0]
            old = new = tsne_source.selected
            tsne_source.trigger('selected', old, new)

    source_fake_nsv = ColumnDataSource(data=dict(value=[]))
    source_fake_nsv.on_change('data', on_slider_change_non_selected_visibility)

    slider_non_selected_visibility.callback = CustomJS(args=dict(source=source_fake_nsv),
                                                       code="""
                                                            source.data = { value: [cb_obj.value] }
                                                            """)

    # change size of non selected points Slider
    slider_non_selected_size = Slider(start=0.5, end=10, value=2, step=0.5, callback_policy='mouseup',
                                      title='Size of not selected points',
                                      width=slider_size[0], height=slider_size[1])

    def on_slider_change_non_selected_size(attrname, old, new):
        global non_selected_points_size
        if len(source_fake_nss.data['value']) > 0:
            non_selected_points_size = source_fake_nss.data['value'][0]
            old = new = tsne_source.selected
            tsne_source.trigger('selected', old, new)

    source_fake_nss = ColumnDataSource(data=dict(value=[]))
    source_fake_nss.on_change('data', on_slider_change_non_selected_size)

    slider_non_selected_size.callback = CustomJS(args=dict(source=source_fake_nss),
                                                 code="""
                                                      source.data = { value: [cb_obj.value] }
                                                      """)

    # change size of selected points Slider
    slider_selected_size = Slider(start=0.5, end=10, value=2, step=0.5, callback_policy='mouseup',
                                  title='Size of selected points',
                                  width=slider_size[0], height=slider_size[1])

    def on_slider_change_selected_size(attrname, old, new):
        global selected_points_size
        if len(source_fake_ss.data['value']) > 0:
            selected_points_size = source_fake_ss.data['value'][0]
            old = new = tsne_source.selected
            tsne_source.trigger('selected', old, new)

    source_fake_ss = ColumnDataSource(data=dict(value=[]))
    source_fake_ss.on_change('data', on_slider_change_selected_size)

    slider_selected_size.callback = CustomJS(args=dict(source=source_fake_ss),
                                             code="""
                                                  source.data = { value: [cb_obj.value] }
                                                  """)

    # -------------------------------------------

    # Layout and session setup ------------------
    # align and make layout
    spike_figure.min_border_top = 50
    spike_figure.min_border_right = 10
    hist_figure.min_border_top = 50
    hist_figure.min_border_left = 10
    tsne_figure.min_border_right = 50

    if k4:
        lay = row(column(tsne_figure,
                         row(slider_non_selected_visibility, slider_non_selected_size, slider_selected_size),
                         row(spike_figure, hist_figure),
                         user_info_edit),
                 column(clusters_table,
                        button_show_clusters_of_selected_points,
                        button_merge_clusters_of_selected_points,
                        button_delete_cluster,
                        select_cluster_to_move_points_to,
                        new_cluster_name_edit,
                        button_show_all_clusters,
                        undo_selected_points_button,
                        heatmap_plot))
    else:
        lay = row(column(tsne_figure,
                         row(spike_figure, hist_figure)),
                  column(row(heatmap_plot, column(slider_non_selected_visibility,
                                                  slider_non_selected_size,
                                                  slider_selected_size)),
                         user_info_edit),
                  column(clusters_table,
                         button_show_clusters_of_selected_points,
                         button_merge_clusters_of_selected_points,
                         button_delete_cluster,
                         select_cluster_to_move_points_to,
                         new_cluster_name_edit,
                         button_show_all_clusters,
                         undo_selected_points_button))


    session = push_session(curdoc())
    session.show(lay)  # open the document in a browser
    session.loop_until_closed()  # run forever, requires stopping the interpreter in order to stop :)
Ejemplo n.º 5
0

def trigger_layout(text, run, ttype, source, edge, trigger, pretrigger):
    lay = column([
        text,
        row([run, ttype, source, edge], sizing_mode=MODE, width=DEFAULT_WIDTH),
        trigger, pretrigger
    ],
                 sizing_mode=MODE)
    return lay


###### CONNECT ##########

but_connect = Toggle(label='Connect', active=True, sizing_mode='stretch_both')
but_connect.on_click(update_but_connect)
app.set_port_closed_callback(update_port_closed)

devices = Dropdown(label="Device", menu=list_ttys(), disabled=False)
devices.on_click(update_devices)

but_refresh = Toggle(label='Refresh', active=True, sizing_mode='stretch_both')
but_refresh.on_click(update_but_refresh)

connect_layout = row([but_connect, devices, but_refresh], sizing_mode=MODE)

###### CHANNEL A ########

text_cha = Div(text='<hr><h2>Channel A</h2>')
on_cha = Toggle(label='On/Off', active=True)
on_cha.on_click(update_on_cha)
Ejemplo n.º 6
0
def checkbox_button_group_handler(active):
    print("checkbox_button_group_handler: %s" % active)


def radio_button_group_handler(active):
    print("radio_button_group_handler: %s" % active)


button = Button(label="Push button",
                icon=Icon(icon_name="check"),
                type="primary")
button.on_click(button_handler)

toggle = Toggle(label="Toggle button", type="success")
toggle.on_click(toggle_handler)

menu = [("Item 1", "item_1"), ("Item 2", "item_2"), None, ("Item 3", "item_3")]
dropdown = Dropdown(label="Dropdown button", type="warning", menu=menu)
dropdown.on_click(dropdown_handler)

menu = [("Item 1", "foo"), ("Item 2", "bar"), None, ("Item 3", "baz")]
split = Dropdown(label="Split button",
                 type="danger",
                 menu=menu,
                 default_value="baz")
split.on_click(split_handler)

checkbox_group = CheckboxGroup(labels=["Option 1", "Option 2", "Option 3"],
                               active=[0, 1])
checkbox_group.on_click(checkbox_group_handler)
Ejemplo n.º 7
0
    print("radio_group_handler: %s" % active)
    session.store_document(document)

def checkbox_button_group_handler(active):
    print("checkbox_button_group_handler: %s" % active)
    session.store_document(document)

def radio_button_group_handler(active):
    print("radio_button_group_handler: %s" % active)
    session.store_document(document)

button = Button(label="Push button", icon=Icon(name="check"), type="primary")
button.on_click(button_handler)

toggle = Toggle(label="Toggle button", type="success")
toggle.on_click(toggle_handler)

menu = [("Item 1", "item_1"), ("Item 2", "item_2"), None, ("Item 3", "item_3")]
dropdown = Dropdown(label="Dropdown button", type="warning", menu=menu)
dropdown.on_click(dropdown_handler)

menu = [("Item 1", "foo"), ("Item 2", "bar"), None, ("Item 3", "baz")]
split = Dropdown(label="Split button", type="danger", menu=menu, default_action="baz")
split.on_click(split_handler)

checkbox_group = CheckboxGroup(labels=["Option 1", "Option 2", "Option 3"], active=[0, 1])
checkbox_group.on_click(checkbox_group_handler)

radio_group = RadioGroup(labels=["Option 1", "Option 2", "Option 3"], active=0)
radio_group.on_click(radio_group_handler)
Ejemplo n.º 8
0
    if active == False:
        filtered_data = dfincident[np.isin(
            dfincident["dim_incident_incident_type"], type_filter.value)]
        update_map(filtered_data)
        slider_active_toggle.label = "slider not active"
        slider_active_toggle.button_type = "default"


# assign callbacks
play_button = Toggle(label="PLAY",
                     button_type="primary",
                     width=100,
                     callback=callback_play)
time_slider.on_change('value', callback_time_slider)
slider_active_toggle.on_click(callback_toggle_slider_activity)
pattern_select.on_change('active', callback_pattern_selection)
aggregate_select.on_change('active', callback_aggregation_selection)
groupby_select.on_change('active', callback_groupby_selection)
type_filter.on_change('value', callback_type_filter)
geo_source.on_change('selected', callback_map_selection)
select_all_types_button.on_click(callback_select_all_types)
## end callbacks

# headers
map_head = Div(text="<h2>Spatial distribution of incidents</h2>",
               css_classes=["plot-head"],
               width=LEFT_COLUMN_WIDTH)
ts_head = Div(text="<h2>Time distribution of incidents</h2>",
              css_classes=["plot-head"],
              width=RIGHT_COLUMN_WIDTH)
Ejemplo n.º 9
0
                value=c.EMPTY_STRING,
                options=[c.EMPTY_STRING] + FILTER_PROPERTIES[c.NC])
NG_HOST = Select(title='Neighbourhood Group:',
                 value=c.EMPTY_STRING,
                 options=[c.EMPTY_STRING] + NG_LIST)
ROOM_TYPE_HOST = Select(title='Room Type:',
                        value=c.EMPTY_STRING,
                        options=[c.EMPTY_STRING] + RT_LIST)

# Radio Button Widget
USER_TYPE = RadioButtonGroup(labels=['Guest', 'Host'], active=0)
USER_TYPE.on_change(c.ACTIVE, update_layout)

# Button Toggle Widget
PREDICT_VALUE = Toggle(label='Submit', button_type='success')
PREDICT_VALUE.on_click(predict_price)

# Text Widget
HOST_PRICE = Paragraph(text="""Select all listing values and press
                        submit to view your listings valued price.""",
                       width=500,
                       height=500)

WIDGETS_BOTH_1 = [USER_TYPE, CITY_INPUT]
WIDGETS_BOTH_2 = [
    ACCOMMODATES_SLIDER, BEDROOM_SLIDER, BED_SLIDER, BATHROOM_SLIDER
]
WIDGETS_BOTH_3 = [AMENITIES_SELECT]

WIDGETS_GUEST = widgetbox(
    WIDGETS_BOTH_1 + [ROOM_TYPE_GROUP, PRICE_SLIDER] + WIDGETS_BOTH_2 +
Ejemplo n.º 10
0
trigger_level_input = TextInput(value="1", title="Trigger Level (V)")
time_range_input = TextInput(value="1000", title="Time Range (us)")
load_file_input = TextInput(value=home_dir, title="Load file:")
save_filepath_input = TextInput(value=home_dir, title="Save to directory:")
force_save_filename_input = TextInput(value=str(dt.now(PT).year) + "_" +
                                      str(dt.now(PT).month) + "_" +
                                      str(dt.now(PT).day) + ".h5",
                                      title="Save as filename:")
save_filepath_PC_input = TextInput(value=home_dir, title="Save to directory:")
save_filename_PC_input = TextInput(value="PC_" + str(dt.now(PT).year) + "_" +
                                   str(dt.now(PT).month) + "_" +
                                   str(dt.now(PT).day) + ".h5",
                                   title="Save as filename:")

#setup event handlers
all_off_button.on_click(lambda x: allOff_ButtonClick())
all_on_button.on_click(lambda x: allOn_ButtonClick())
reset_button.on_click(lambda x: reset_ButtonClick())
autoscale_button.on_click(lambda x: autoscale_ButtonClick())
channel_button[0].on_click(lambda x: channel_ButtonClick(1))
channel_button[1].on_click(lambda x: channel_ButtonClick(2))
channel_button[2].on_click(lambda x: channel_ButtonClick(3))
channel_button[3].on_click(lambda x: channel_ButtonClick(4))
pulse_capture_button.on_click(lambda x: pulseCapture_ButtonClick())
auto_save_button.on_click(lambda x: autoSave_ButtonClick())
load_button.on_click(lambda x: load_ButtonClick())
force_save_button.on_click(lambda x: forceSave_ButtonClick())
save_PC_button.on_click(lambda x: savePC_ButtonClick())

acq_period_select.on_change("value", acqPeriod_SelectChange)
channel_name_select1.on_change("value", chanNameSelect1_SelectChange)
Ejemplo n.º 11
0
class MapWidgets:
    def __init__(self, tweet_data_controller, config):
        self.tweet_data_controller = tweet_data_controller
        self.config = config

        self.toggle_data = Toggle(label="Toggle Data", active=False, width=100)
        self.toggle_data.on_click(self.toggle_data_callback)

        self.button_clear = Button(label="Clear", width=100)
        self.button_clear.on_click(self.button_clear_callback)

        self.toggle_sde_ellipse = Toggle(label="Toggle Ellipse",
                                         active=False,
                                         width=150)
        self.toggle_sde_ellipse.on_click(self.toggle_sde_ellipse_callback)

        self.toggle_sibling_ellipses = Toggle(label="Toggle Siblings",
                                              active=False,
                                              width=150)
        self.toggle_sibling_ellipses.on_click(
            self.toggle_sibling_ellipses_callback)

        self.toggle_dissolve = Toggle(label="Toggle Dissolve",
                                      active=False,
                                      width=150)
        self.toggle_dissolve.on_click(self.toggle_dissolve_callback)

        self.text_selection_details = Div(text='Selected ID:')

        self.text_input = TextInput(value="-1", title="Enter ID:", width=150)

        self.button_find = Button(label="Find ID", width=100)
        self.button_find.on_click(self.button_find_callback)

        self.radio_button_data_type = RadioButtonGroup(
            labels=['all', 'working', 'non-working'], active=1, width=300)
        self.radio_button_data_type.on_change(
            'active', self.radio_button_data_type_change)

        self.range_slider_count = RangeSlider(start=config.count[0],
                                              end=config.count[1],
                                              value=(config.count[0],
                                                     config.count[1]),
                                              step=config.count[2],
                                              title="Sibling Count")
        self.range_slider_count.on_change('value',
                                          self.range_slider_count_change)
        self.button_count_start_minus = Button(label="-", width=15)
        self.button_count_start_minus.on_click(
            self.button_count_start_minus_callback)
        self.button_count_start_plus = Button(label="+", width=15)
        self.button_count_start_plus.on_click(
            self.button_count_start_plus_callback)
        self.button_count_end_minus = Button(label="-", width=15)
        self.button_count_end_minus.on_click(
            self.button_count_end_minus_callback)
        self.button_count_end_plus = Button(label="+", width=15)
        self.button_count_end_plus.on_click(
            self.button_count_end_plus_callback)
        self.tweet_data_controller.count_values = self.range_slider_count

        self.range_slider_area = RangeSlider(start=config.area[0],
                                             end=config.area[1],
                                             value=(config.area[0],
                                                    config.area[1]),
                                             step=config.area[2],
                                             title="Area Size")
        self.range_slider_area.on_change('value',
                                         self.range_slider_area_change)
        self.button_area_start_minus = Button(label="-", width=15)
        self.button_area_start_minus.on_click(
            self.button_area_start_minus_callback)
        self.button_area_start_plus = Button(label="+", width=15)
        self.button_area_start_plus.on_click(
            self.button_area_start_plus_callback)
        self.button_area_end_minus = Button(label="-", width=15)
        self.button_area_end_minus.on_click(
            self.button_area_end_minus_callback)
        self.button_area_end_plus = Button(label="+", width=15)
        self.button_area_end_plus.on_click(self.button_area_end_plus_callback)
        self.tweet_data_controller.area_values = self.range_slider_area

        self.range_slider_distance = RangeSlider(start=config.distance[0],
                                                 end=config.distance[1],
                                                 value=(config.distance[0],
                                                        config.distance[1]),
                                                 step=config.distance[2],
                                                 title="Distance")
        self.range_slider_distance.on_change('value',
                                             self.range_slider_distance_change)
        self.button_distance_start_minus = Button(label="-", width=15)
        self.button_distance_start_minus.on_click(
            self.button_distance_start_minus_callback)
        self.button_distance_start_plus = Button(label="+", width=15)
        self.button_distance_start_plus.on_click(
            self.button_distance_start_plus_callback)
        self.button_distance_end_minus = Button(label="-", width=15)
        self.button_distance_end_minus.on_click(
            self.button_distance_end_minus_callback)
        self.button_distance_end_plus = Button(label="+", width=15)
        self.button_distance_end_plus.on_click(
            self.button_distance_end_plus_callback)
        self.tweet_data_controller.distance_values = self.range_slider_distance

        self.range_slider_ratio = RangeSlider(start=config.ratio[0],
                                              end=config.ratio[1],
                                              value=(config.ratio[0],
                                                     config.ratio[1]),
                                              step=config.ratio[2],
                                              title="Ratio")
        self.range_slider_ratio.on_change('value',
                                          self.range_slider_ratio_change)
        self.button_ratio_start_minus = Button(label="-", width=15)
        self.button_ratio_start_minus.on_click(
            self.button_ratio_start_minus_callback)
        self.button_ratio_start_plus = Button(label="+", width=15)
        self.button_ratio_start_plus.on_click(
            self.button_ratio_start_plus_callback)
        self.button_ratio_end_minus = Button(label="-", width=15)
        self.button_ratio_end_minus.on_click(
            self.button_ratio_end_minus_callback)
        self.button_ratio_end_plus = Button(label="+", width=15)
        self.button_ratio_end_plus.on_click(
            self.button_ratio_end_plus_callback)
        self.tweet_data_controller.ratio_values = self.range_slider_ratio

        self.range_slider_dissolve = RangeSlider(start=config.dissolve[0],
                                                 end=config.dissolve[1],
                                                 value=(config.dissolve[0],
                                                        config.dissolve[1]),
                                                 step=config.dissolve[2],
                                                 title="Dissolve Area")
        self.range_slider_dissolve.on_change('value',
                                             self.range_slider_dissolve_change)
        self.button_dissolve_start_minus = Button(label="-", width=20)
        self.button_dissolve_start_minus.on_click(
            self.button_dissolve_start_minus_callback)
        self.button_dissolve_start_plus = Button(label="+", width=20)
        self.button_dissolve_start_plus.on_click(
            self.button_dissolve_start_plus_callback)
        self.button_dissolve_end_minus = Button(label="-", width=20)
        self.button_dissolve_end_minus.on_click(
            self.button_dissolve_end_minus_callback)
        self.button_dissolve_end_plus = Button(label="+", width=20)
        self.button_dissolve_end_plus.on_click(
            self.button_dissolve_end_plus_callback)
        self.tweet_data_controller.dissolve_values = self.range_slider_dissolve

        self.filters_active = CheckboxGroup(labels=[
            "Filter by Sibling Count", "Filter by Area", "Filter by Distance",
            "Filter by Ratio", "Filter by Dissolve Area"
        ],
                                            active=[0, 1, 2, 3, 4])
        self.filters_active.on_change('active', self.filters_active_change)

        self.toggle_blend = Toggle(label="Toggle Blend",
                                   active=False,
                                   width=150)
        self.toggle_blend.on_click(self.toggle_blend_callback)
        self.toggle_blend.disabled = False
        self.tweet_data_controller.blend_active = True

        self.slider_blend = Slider(start=0.0,
                                   end=1.0,
                                   value=0.5,
                                   step=0.025,
                                   title="Blend Ratio")
        self.slider_blend.on_change('value', self.slider_blend_change)
        self.slider_blend.disabled = True

        self.text_count = Paragraph(text="")
        self.tweet_data_controller.tc = self.text_count
        self.tweet_data_controller.update_text_count()

        self.toggle_legend = Toggle(label="Toggle Legend",
                                    active=False,
                                    width=100)
        self.toggle_legend.on_click(self.toggle_legend_callback)

        self.button_reset_filters = Button(label="Reset Filters", width=100)
        self.button_reset_filters.on_click(self.button_reset_filters_callback)

    def toggle_data_callback(self, arg):
        #print("Toggle Data: Callback:")
        if arg:
            self.tweet_data_controller.toggle_data(True)
        else:
            self.tweet_data_controller.toggle_data(False)

    def toggle_sde_ellipse_callback(self, arg):
        #print("Toggle Ellipse: Callback: " + str(self.tweet_data_controller.circle_id) + " : " + str(self.tweet_data_controller.circle_idx))
        if arg:
            self.tweet_data_controller.update_sde_ellipse()
            self.tweet_data_controller.update_siblings()
        else:
            self.tweet_data_controller.clear_sde_ellipse()
            self.tweet_data_controller.clear_siblings()

    def toggle_sibling_ellipses_callback(self, arg):
        if arg:
            self.tweet_data_controller.update_sibling_ellipses()
        else:
            self.tweet_data_controller.clear_sibling_ellipses()

    def toggle_dissolve_callback(self, arg):
        #print("Toggle Dissolve: Callback: " + str(self.tweet_data_controller.circle_id) + " : " + str(self.tweet_data_controller.circle_idx))
        if arg:
            self.tweet_data_controller.update_dissolve()
        else:
            self.tweet_data_controller.clear_dissolve()

    def radio_button_data_type_change(self, attrname, old, new):
        if new == 0:
            self.toggle_blend.disabled = True
            self.toggle_blend.active = False
            self.tweet_data_controller.blend_active = False
        else:
            self.toggle_blend.disabled = False
            self.toggle_blend.active = False
            self.tweet_data_controller.blend_active = False

        self.tweet_data_controller.switch_tweet_dataset(new)

        if self.toggle_sde_ellipse.active:
            self.tweet_data_controller.update_sde_ellipse()
            self.tweet_data_controller.update_siblings()
        else:
            self.tweet_data_controller.clear_sde_ellipse()
            self.tweet_data_controller.clear_siblings()

        if self.toggle_sibling_ellipses.active:
            self.tweet_data_controller.update_sibling_ellipses()
        else:
            self.tweet_data_controller.clear_sibling_ellipses()

        if self.toggle_dissolve.active:
            self.tweet_data_controller.update_dissolve()
        else:
            self.tweet_data_controller.clear_dissolve()

        self.tweet_data_controller.clear_find_circle()

    def button_find_callback(self):
        id_value = int(self.text_input.value)
        print("Button Find: Callback: " + str(id_value))
        self.tweet_data_controller.find_id(id_value)

    def button_clear_callback(self):
        id_value = int(self.text_input.value)
        print("Button Clear: Callback: " + str(id_value))
        self.tweet_data_controller.clear_all()
        self.toggle_sde_ellipse.active = False
        self.toggle_sibling_ellipses.active = False
        self.toggle_dissolve.active = False
        self.toggle_blend.active = False

    def range_slider_count_change(self, attrname, old, new):
        start_count = int(new[0])
        end_count = int(new[1])
        self.tweet_data_controller.filter_circles_by_count(
            start_count, end_count)
        self.tweet_data_controller.clear_all()

    def button_count_start_minus_callback(self):
        value_start = int(self.range_slider_count.value[0])
        value_end = int(self.range_slider_count.value[1])

        if value_start > self.range_slider_count.start:
            value_start -= 1

        new_values = [value_start, value_end]
        self.range_slider_count.value = new_values

        self.tweet_data_controller.filter_circles_by_count(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_count_start_plus_callback(self):
        value_start = int(self.range_slider_count.value[0])
        value_end = int(self.range_slider_count.value[1])

        if value_start < value_end:
            value_start += 1

        new_values = [value_start, value_end]
        self.range_slider_count.value = new_values

        self.tweet_data_controller.filter_circles_by_count(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_count_end_minus_callback(self):
        value_start = int(self.range_slider_count.value[0])
        value_end = int(self.range_slider_count.value[1])

        if value_end > value_start:
            value_end -= 1

        new_values = [value_start, value_end]
        self.range_slider_count.value = new_values

        self.tweet_data_controller.filter_circles_by_count(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_count_end_plus_callback(self):
        value_start = int(self.range_slider_count.value[0])
        value_end = int(self.range_slider_count.value[1])

        if value_end < self.range_slider_count.end:
            value_end += 1

        new_values = [value_start, value_end]
        self.range_slider_count.value = new_values

        self.tweet_data_controller.filter_circles_by_count(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def range_slider_area_change(self, attrname, old, new):
        start_area = int(new[0])
        end_area = int(new[1])
        self.tweet_data_controller.filter_circles_by_area(start_area, end_area)
        self.tweet_data_controller.clear_all()

    def button_area_start_minus_callback(self):
        value_start = int(self.range_slider_area.value[0])
        value_end = int(self.range_slider_area.value[1])

        if value_start > self.range_slider_area.start:
            value_start -= int(self.config.area[2])

        new_values = [value_start, value_end]
        self.range_slider_area.value = new_values

        self.tweet_data_controller.filter_circles_by_area(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_area_start_plus_callback(self):
        value_start = int(self.range_slider_area.value[0])
        value_end = int(self.range_slider_area.value[1])

        if value_start < value_end:
            value_start += int(self.config.area[2])

        new_values = [value_start, value_end]
        self.range_slider_area.value = new_values

        self.tweet_data_controller.filter_circles_by_area(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_area_end_minus_callback(self):
        value_start = int(self.range_slider_area.value[0])
        value_end = int(self.range_slider_area.value[1])

        if value_end > value_start:
            value_end -= int(self.config.area[2])

        new_values = [value_start, value_end]
        self.range_slider_area.value = new_values

        self.tweet_data_controller.filter_circles_by_area(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_area_end_plus_callback(self):
        value_start = int(self.range_slider_area.value[0])
        value_end = int(self.range_slider_area.value[1])

        if value_end < self.range_slider_area.end:
            value_end += int(self.config.area[2])

        new_values = [value_start, value_end]
        self.range_slider_area.value = new_values

        self.tweet_data_controller.filter_circles_by_area(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def range_slider_distance_change(self, attrname, old, new):
        start_distance = int(new[0])
        end_distance = int(new[1])
        self.tweet_data_controller.filter_circles_by_distance(
            start_distance, end_distance)
        self.tweet_data_controller.clear_all()

    def button_distance_start_minus_callback(self):
        value_start = int(self.range_slider_distance.value[0])
        value_end = int(self.range_slider_distance.value[1])

        if value_start > self.range_slider_distance.start:
            value_start -= 1

        new_values = [value_start, value_end]
        self.range_slider_distance.value = new_values

        self.tweet_data_controller.filter_circles_by_distance(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_distance_start_plus_callback(self):
        value_start = int(self.range_slider_distance.value[0])
        value_end = int(self.range_slider_distance.value[1])

        if value_start < value_end:
            value_start += 1

        new_values = [value_start, value_end]
        self.range_slider_distance.value = new_values

        self.tweet_data_controller.filter_circles_by_distance(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_distance_end_minus_callback(self):
        value_start = int(self.range_slider_distance.value[0])
        value_end = int(self.range_slider_distance.value[1])

        if value_end > value_start:
            value_end -= 1

        new_values = [value_start, value_end]
        self.range_slider_distance.value = new_values

        self.tweet_data_controller.filter_circles_by_distance(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_distance_end_plus_callback(self):
        value_start = int(self.range_slider_distance.value[0])
        value_end = int(self.range_slider_distance.value[1])

        if value_end < self.range_slider_distance.end:
            value_end += 1

        new_values = [value_start, value_end]
        self.range_slider_distance.value = new_values

        self.tweet_data_controller.filter_circles_by_distance(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def range_slider_ratio_change(self, attrname, old, new):
        start_ratio = new[0]
        end_ratio = new[1]
        self.tweet_data_controller.filter_circles_by_ratio(
            start_ratio, end_ratio)
        self.tweet_data_controller.clear_all()

    def button_ratio_start_minus_callback(self):
        value_start = self.range_slider_ratio.value[0]
        value_end = self.range_slider_ratio.value[1]

        if value_start > self.range_slider_ratio.start:
            value_start -= 0.1
            # This is a hack to prevent the end value to reaching -1.1
            if value_start < self.range_slider_ratio.start:
                value_start = self.range_slider_ratio.start

        new_values = [value_start, value_end]
        self.range_slider_ratio.value = new_values

        self.tweet_data_controller.filter_circles_by_ratio(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_ratio_start_plus_callback(self):
        value_start = self.range_slider_ratio.value[0]
        value_end = self.range_slider_ratio.value[1]

        if value_start < value_end:
            value_start += 0.1

        new_values = [value_start, value_end]
        self.range_slider_ratio.value = new_values

        self.tweet_data_controller.filter_circles_by_ratio(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_ratio_end_minus_callback(self):
        value_start = self.range_slider_ratio.value[0]
        value_end = self.range_slider_ratio.value[1]

        if value_end > value_start:
            value_end -= 0.1

        new_values = [value_start, value_end]
        self.range_slider_ratio.value = new_values

        self.tweet_data_controller.filter_circles_by_ratio(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_ratio_end_plus_callback(self):
        value_start = self.range_slider_ratio.value[0]
        value_end = self.range_slider_ratio.value[1]

        if value_end < self.range_slider_ratio.end:
            value_end += 0.1
            # This is a hack to prevent the end value to reaching 100.1
            if value_end > self.range_slider_ratio.end:
                value_end = self.range_slider_ratio.end

        new_values = [value_start, value_end]
        self.range_slider_ratio.value = new_values

        self.tweet_data_controller.filter_circles_by_ratio(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def range_slider_dissolve_change(self, attrname, old, new):
        start_dissolve = new[0]
        end_dissolve = new[1]
        self.tweet_data_controller.filter_circles_by_dissolve(
            start_dissolve, end_dissolve)
        self.tweet_data_controller.clear_all()

    def button_dissolve_start_minus_callback(self):
        value_start = self.range_slider_dissolve.value[0]
        value_end = self.range_slider_dissolve.value[1]

        if value_start > self.range_slider_dissolve.start:
            value_start -= 1

        new_values = [value_start, value_end]
        self.range_slider_dissolve.value = new_values

        self.tweet_data_controller.filter_circles_by_dissolve(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_dissolve_start_plus_callback(self):
        value_start = self.range_slider_dissolve.value[0]
        value_end = self.range_slider_dissolve.value[1]

        if value_start < value_end:
            value_start += 1

        new_values = [value_start, value_end]
        self.range_slider_dissolve.value = new_values

        self.tweet_data_controller.filter_circles_by_dissolve(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_dissolve_end_minus_callback(self):
        value_start = self.range_slider_dissolve.value[0]
        value_end = self.range_slider_dissolve.value[1]

        if value_end > value_start:
            value_end -= 1

        new_values = [value_start, value_end]
        self.range_slider_dissolve.value = new_values

        self.tweet_data_controller.filter_circles_by_dissolve(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def button_dissolve_end_plus_callback(self):
        value_start = self.range_slider_dissolve.value[0]
        value_end = self.range_slider_dissolve.value[1]

        if value_end < self.range_slider_dissolve.end:
            value_end += 1

        new_values = [value_start, value_end]
        self.range_slider_dissolve.value = new_values

        self.tweet_data_controller.filter_circles_by_dissolve(
            value_start, value_end)
        self.tweet_data_controller.clear_all()

    def filters_active_change(self, attrname, old, new):
        self.tweet_data_controller.filters_active(new)

    def toggle_blend_callback(self, arg):
        #print("Toggle Blend: Callback: " + str(self.tweet_data_controller.circle_id) + " : " + str(self.tweet_data_controller.circle_idx))
        if arg:
            self.tweet_data_controller.turn_blend_on(
                self.toggle_sde_ellipse.active,
                self.toggle_sibling_ellipses.active,
                self.toggle_dissolve.active)
            self.slider_blend.disabled = False
        else:
            self.tweet_data_controller.turn_blend_off()
            self.slider_blend.disabled = True

    def slider_blend_change(self, attrname, old, new):
        self.tweet_data_controller.blend(new, self.toggle_sde_ellipse.active,
                                         self.toggle_sibling_ellipses.active,
                                         self.toggle_dissolve.active)

    def toggle_legend_callback(self, arg):
        self.tweet_data_controller.toggle_legend(arg)

    def button_reset_filters_callback(self):
        print("Button Reset Filters:")
        values_reset = [self.config.count[0], self.config.count[1]]
        self.tweet_data_controller.filter_circles_by_count(
            self.config.count[0], self.config.count[1])
        self.range_slider_count.value = values_reset
        values_reset = [self.config.area[0], self.config.area[1]]
        self.tweet_data_controller.filter_circles_by_area(
            self.config.area[0], self.config.area[1])
        self.range_slider_area.value = values_reset
        values_reset = [self.config.distance[0], self.config.distance[1]]
        self.tweet_data_controller.filter_circles_by_distance(
            self.config.distance[0], self.config.distance[1])
        self.range_slider_distance.value = values_reset
        values_reset = [self.config.ratio[0], self.config.ratio[1]]
        self.tweet_data_controller.filter_circles_by_ratio(
            self.config.ratio[0], self.config.ratio[1])
        self.range_slider_ratio.value = values_reset
        values_reset = [self.config.dissolve[0], self.config.dissolve[1]]
        self.tweet_data_controller.filter_circles_by_dissolve(
            self.config.dissolve[0], self.config.dissolve[1])
        self.range_slider_dissolve.value = values_reset
        self.tweet_data_controller.clear_all()
# Text inputs
channel_name_input = TextInput(title="Name:")
trigger_level_input = TextInput(value="1", title="Trigger Level (V)")
time_range_input = TextInput(value="1000", title="Time Range (us)")
load_file_input = TextInput(value=home_dir, title="Load file:")
save_filepath_input = TextInput(value=home_dir, title="Save to directory:")
force_save_filename_input = TextInput(value=str(dt.now(PT).year)+"_"+str(dt.now(PT).month)+"_"+str(dt.now(PT).day)+".h5", 
                                title="Save as filename:")
save_filepath_PC_input = TextInput(value=home_dir, title="Save to directory:")
save_filename_PC_input = TextInput(value="PC_"+str(dt.now(PT).year)+"_"+str(dt.now(PT).month)+"_"+str(dt.now(PT).day)+".h5",
                                title="Save as filename:")


#setup event handlers
all_off_button.on_click(lambda x: allOff_ButtonClick())
all_on_button.on_click(lambda x: allOn_ButtonClick())
reset_button.on_click(lambda x: reset_ButtonClick())
autoscale_button.on_click(lambda x: autoscale_ButtonClick())
channel_button[0].on_click(lambda x: channel_ButtonClick(1))
channel_button[1].on_click(lambda x: channel_ButtonClick(2))
channel_button[2].on_click(lambda x: channel_ButtonClick(3))
channel_button[3].on_click(lambda x: channel_ButtonClick(4))
pulse_capture_button.on_click(lambda x: pulseCapture_ButtonClick())
auto_save_button.on_click(lambda x: autoSave_ButtonClick())
load_button.on_click(lambda x: load_ButtonClick())
force_save_button.on_click(lambda x: forceSave_ButtonClick())
save_PC_button.on_click(lambda x: savePC_ButtonClick())

acq_period_select.on_change("value", acqPeriod_SelectChange)
channel_name_select1.on_change("value", chanNameSelect1_SelectChange)
Ejemplo n.º 13
0
class Dashboard:
    def __init__(self, default_dir, port):
        # path to source directory
        self.src_dir = os.path.dirname(os.path.abspath(__file__))
        # MD directory and files selection
        self.md_dir = TextInput(
            title="Path to MD directory containing mdin and mdout files",
            value="",
            width=750)
        self.anim_button = Toggle(label="▶ Load",
                                  button_type="warning",
                                  width=80,
                                  height=50,
                                  active=False)
        self.port = port
        # container for the buttons that are created while the user types in the textinput
        self.autocomp_results = column(children=[])
        # file used to display temperature, pressure...etc. plots
        self.mdout_sel = Select(
            title="MDout file",
            width=230,
            value=None,
            options=[],
        )
        # button to load content
        self.mdout_button = Button(width=80,
                                   height=50,
                                   label="Plot",
                                   button_type="primary")
        self.mdout_files = [None]
        self.md_mdout_files = []
        # mdinfo figures
        progressbar_tooltip = """
        <span style="color:#428df5">@completed{0,0}</span> out of <span style="color:#428df5">@total{0,0}</span> steps (<span style="color:#428df5">@remaining{0,0}</span> remaining)
        """
        self.progressbar = figure(title="Current progress",
                                  x_range=Range1d(0, 10),
                                  tooltips=progressbar_tooltip,
                                  height=70,
                                  width=350,
                                  tools="hover",
                                  toolbar_location=None)
        self.progressbar.xgrid.grid_line_color = None
        self.progressbar.ygrid.grid_line_color = None
        self.progressbar.axis.visible = False
        self.progressbar.outline_line_color = "#444444"
        self.steps_CDS = ColumnDataSource({
            "total": [np.nan],
            "completed": [np.nan],
            "remaining": [np.nan],
            "color": ['#428df5'],
        })
        self.progressbar.hbar(
            y=0,
            left=0,
            right="completed",
            source=self.steps_CDS,
            height=0.5,
            color="color",
        )
        self.progressbar.hover[0].mode = "hline"

        self.calc_speed = Div(width=150,
                              height=50,
                              text="Calculation speed:",
                              style={
                                  "font-weight": "bold",
                                  "color": "#444444",
                                  "margin-top": "5px"
                              })

        self.eta = Div(width=280,
                       height=50,
                       text="Estimated time remaining:",
                       style={
                           "font-weight": "bold",
                           "color": "#444444",
                           "margin-top": "5px"
                       })

        self.last_update = Div(width=280,
                               height=50,
                               text="Last update:",
                               style={
                                   "font-weight": "bold",
                                   "color": "#444444",
                                   "margin-top": "5px"
                               })

        # number of mdout files displayed on the dashboard at max
        self.slider = Slider(start=1,
                             end=12,
                             value=2,
                             step=1,
                             callback_policy="mouseup",
                             title="Number of simulations displayed")
        self.dashboard_CDS = ColumnDataSource({
            "y_coords": [0, 1],
            "mdout": ["heat.out", "prod.out"],
            "time": [42, 200],
            "angle": [1.09, 5.193],
            "color": [sim_palette[0], sim_palette[1]],
        })
        dashboard_tooltip = """
        <span style="color:@color">@mdout</span>: @time{0,0.00} ns
        """
        # pie plot
        self.pie = figure(plot_height=300,
                          width=500,
                          title="Simulations length",
                          toolbar_location=None,
                          tools="hover",
                          tooltips=dashboard_tooltip,
                          x_range=Range1d(-0.5, 1.0))

        self.rpie = self.pie.wedge(x=0,
                                   y=1,
                                   radius=0.4,
                                   source=self.dashboard_CDS,
                                   start_angle=cumsum('angle',
                                                      include_zero=True),
                                   end_angle=cumsum('angle'),
                                   line_color="white",
                                   fill_color='color',
                                   legend="mdout")
        self.pie.axis.axis_label = None
        self.pie.axis.visible = False
        self.pie.grid.grid_line_color = None
        self.pie.legend.label_text_font_size = '9pt'
        self.pie.legend.border_line_width = 0
        self.pie.legend.border_line_alpha = 0
        self.pie.legend.spacing = 0
        self.pie.legend.margin = 0
        # hbar plot
        self.bar = figure(width=850,
                          plot_height=300,
                          toolbar_location=None,
                          tools="hover",
                          tooltips=dashboard_tooltip)
        self.rbar = self.bar.hbar(y="y_coords",
                                  left=0,
                                  right="time",
                                  source=self.dashboard_CDS,
                                  height=0.8,
                                  color="color")
        self.bar.x_range.set_from_json("start", 0)
        self.bar.xaxis.axis_label = "Time (ns)"
        self.bar.yaxis.axis_label = None
        self.bar.yaxis.visible = False
        self.bar.hover[0].mode = "hline"

        ## Mdout figures
        self.mdinfo_CDS = ColumnDataSource(copy.deepcopy(empty_mddata_dic))
        self.moving_avg_trans = CustomJSTransform(v_func=moving_avg_func)
        ticker = PrintfTickFormatter(format="%4.0e")
        # Temperature
        self.temperature_fig = figure(
            plot_height=size[1],
            plot_width=size[0],
            active_scroll="wheel_zoom",
        )
        self.temperature_fig.toolbar.autohide = True
        self.temperature_fig.xaxis.axis_label = "Number of steps"
        self.temperature_fig.yaxis.axis_label = "Temperature (K)"
        self.temperature_fig.xaxis.formatter = ticker
        r = self.temperature_fig.line("Nsteps",
                                      "Temperature",
                                      color=palette[0],
                                      source=self.mdinfo_CDS,
                                      _width=1,
                                      alpha=0.15)
        self.temperature_fig.line(transform("Nsteps", self.moving_avg_trans),
                                  transform("Temperature",
                                            self.moving_avg_trans),
                                  color=colorscale(palette[0], 0.85),
                                  source=self.mdinfo_CDS,
                                  line_width=3)
        self.temperature_fig.add_tools(make_hover([r]))

        # Pressure
        self.pressure_fig = figure(
            plot_height=size[1],
            plot_width=size[0],
            active_scroll="wheel_zoom",
        )
        self.pressure_fig.toolbar.autohide = True
        self.pressure_fig.xaxis.axis_label = "Number of steps"
        self.pressure_fig.yaxis.axis_label = "Pressure"
        self.pressure_fig.xaxis.formatter = ticker
        r = self.pressure_fig.line("Nsteps",
                                   "Pressure",
                                   color=palette[1],
                                   source=self.mdinfo_CDS,
                                   line_width=1,
                                   alpha=0.15)
        self.pressure_fig.line(transform("Nsteps", self.moving_avg_trans),
                               transform("Pressure", self.moving_avg_trans),
                               color=colorscale(palette[1], 0.85),
                               source=self.mdinfo_CDS,
                               line_width=3)
        self.pressure_fig.add_tools(make_hover([r]))

        # Energy
        self.energy_fig = figure(
            plot_height=size[1],
            plot_width=size[0],
            active_scroll="wheel_zoom",
        )
        etot = self.energy_fig.line("Nsteps",
                                    "Etot",
                                    color=palette[2],
                                    source=self.mdinfo_CDS,
                                    line_width=1)
        ektot = self.energy_fig.line("Nsteps",
                                     "EKtot",
                                     color=palette[3],
                                     source=self.mdinfo_CDS,
                                     line_width=1)
        eptot = self.energy_fig.line("Nsteps",
                                     "EPtot",
                                     color=palette[4],
                                     source=self.mdinfo_CDS,
                                     line_width=1)
        legend = Legend(items=[
            ("Total", [etot]),
            ("Kinetic", [ektot]),
            ("Potential", [eptot]),
        ],
                        location="top_right")
        self.energy_fig.add_layout(legend, 'right')
        self.energy_fig.add_tools(make_hover([etot]))
        self.energy_fig.legend.location = "top_left"
        self.energy_fig.legend.click_policy = "hide"
        self.energy_fig.toolbar.autohide = True
        self.energy_fig.xaxis.axis_label = "Number of steps"
        self.energy_fig.yaxis.axis_label = "Energy"
        self.energy_fig.xaxis.formatter = ticker

        # Volume
        self.vol_fig = figure(
            plot_height=size[1],
            plot_width=size[0],
            active_scroll="wheel_zoom",
        )
        self.vol_fig.toolbar.autohide = True
        self.vol_fig.xaxis.axis_label = "Number of steps"
        self.vol_fig.yaxis.axis_label = "Volume"
        self.vol_fig.xaxis.formatter = ticker
        r = self.vol_fig.line("Nsteps",
                              "Volume",
                              color=palette[6],
                              source=self.mdinfo_CDS,
                              line_width=1,
                              alpha=0.15)
        self.vol_fig.line(transform("Nsteps", self.moving_avg_trans),
                          transform("Volume", self.moving_avg_trans),
                          color=colorscale(palette[6], 0.85),
                          source=self.mdinfo_CDS,
                          line_width=3)
        self.vol_fig.add_tools(make_hover([r]))

        # Density
        self.density_fig = figure(
            plot_height=size[1],
            plot_width=size[0],
            active_scroll="wheel_zoom",
        )
        self.density_fig.toolbar.autohide = True
        self.density_fig.xaxis.axis_label = "Number of steps"
        self.density_fig.yaxis.axis_label = "Density"
        self.density_fig.xaxis.formatter = ticker
        r = self.density_fig.line("Nsteps",
                                  "Density",
                                  color=palette[7],
                                  source=self.mdinfo_CDS,
                                  line_width=1,
                                  alpha=0.15)
        self.density_fig.line(transform("Nsteps", self.moving_avg_trans),
                              transform("Density", self.moving_avg_trans),
                              color=colorscale(palette[7], 0.85),
                              source=self.mdinfo_CDS,
                              line_width=3)
        self.density_fig.add_tools(make_hover([r]))

        ## RMSD figure
        self.empty_rmsd_dic = {k: [] for k in ["Time", "RMSD"]}
        self.rmsd_CDS = ColumnDataSource(self.empty_rmsd_dic)
        self.rmsd_fig = figure(
            plot_height=size[1],
            plot_width=size[0],
            active_scroll="wheel_zoom",
        )
        self.rmsd_fig.toolbar.autohide = True
        self.rmsd_fig.xaxis.axis_label = "Time (ps)"
        self.rmsd_fig.yaxis.axis_label = "RMSD (Å)"
        self.rmsd_fig.xaxis.formatter = ticker
        r = self.rmsd_fig.line("Time",
                               "RMSD",
                               color=palette[8],
                               source=self.rmsd_CDS,
                               line_width=2)
        self.rmsd_fig.add_tools(
            make_hover([r],
                       tooltips=[("Time (ps)", "@Time{0,0}"),
                                 ("RMSD (Å)", "@RMSD")]))
        self.rmsd_button = Button(width=100,
                                  label="Calculate RMSD",
                                  button_type="primary")
        self.trajectory = MultiSelect(
            title="Trajectory file(s)",
            width=400,
            value=None,
            options=[],
        )
        self.rst_traj = Select(
            title="Restart file",
            width=400,
            value=None,
            options=[],
        )
        self.topology = Select(
            title="Topology file",
            width=200,
            value=None,
            options=[],
        )
        self.mask = TextInput(title="Mask",
                              value="protein@CA,C,O,N",
                              width=200)
        # NGLview
        self.last_rst_update = 0
        self.view_button = Button(width=80,
                                  label="Visualize",
                                  button_type="primary")
        self.view_canvas = Div(width=size[0],
                               height=size[1] + 60,
                               css_classes=["ngldiv"],
                               text="")
        self.ngl_help_div = Div(width=0, height=0, text="")
        self.ngl_help_button = Toggle(width=80, label="Help", active=False)
        self.ngl_lig = TextInput(title="Ligand name", value="LIG", width=80)
        self.ngl_representations = CheckboxButtonGroup(
            labels=["Protein", "Ligand", "Water", "Lipids", "Ions"],
            active=[0, 1, 2, 3, 4],
        )
        # info about simulation files (min, dt, rst and mdcrd files)
        self.mdout_info = {}
        # add callbacks
        self.add_callbacks()
        self.md_dir.value = default_dir

    def ngl_help(self, new_value):
        """Show help on NGL controls"""
        if self.ngl_help_button.active:
            self.ngl_help_div.width = 300
            self.ngl_help_div.height = size[1]
            self.ngl_help_button.label = "Hide"
            self.ngl_help_div.text = NGL_HELP_TEXT
        else:
            self.ngl_help_div.width = 300
            self.ngl_help_div.height = size[1]
            self.ngl_help_button.label = "Help"
            self.ngl_help_div.text = ""

    def autocomp_callback(self, attr, old, new):
        """List all directories for the current typed path, output as buttons to click"""
        path = os.path.join(new + "*", "")
        opts = [] if new == "" else glob.glob(path)
        opts = sorted(opts, key=lambda x: x.split("/")[-2].lower())
        buttons = [Button(width=750, label=opt) for opt in opts]
        for b in buttons:
            cb = CustomJS(args=dict(md_dir=self.md_dir, button=b),
                          code="""
            md_dir.value_input = button.label;
            md_dir.value = button.label;
            """)
            b.js_on_click(cb)
        self.autocomp_results.children = buttons
        mdinfo_file = os.path.join(new, "mdinfo")
        if os.path.exists(mdinfo_file):
            self.anim_button.button_type = "success"
        else:
            self.anim_button.button_type = "warning"

    def traj_top_callback(self, attr, old, new):
        log.debug(f"Updating list of trajectory and topology files")
        try:
            # search netcdf files
            traj = [
                f for f in os.listdir(self.md_dir.value)
                if re.search(r'.+\.n(et)?c(df)?$', f)
            ]
            traj.sort(key=lambda f: os.path.getmtime(
                os.path.join(self.md_dir.value, f)),
                      reverse=True)
            self.trajectory.options = traj
            # search restart files
            restart = [
                f for f in os.listdir(self.md_dir.value)
                if re.search(r'.+\.rst7?$', f)
            ]
            restart.sort(key=lambda f: os.path.getmtime(
                os.path.join(self.md_dir.value, f)),
                         reverse=True)
            self.rst_traj.options = restart
            if self.rst_traj.options:
                self.rst_traj.value = self.rst_traj.options[0]
            # search for .top, .prmtop, .parm7 or .prm
            top = [
                f for f in os.listdir(self.md_dir.value)
                if re.search(r'.+\.(prm)?top$', f)
                or re.search(r'.+\.pa?rm7?$', f)
            ]
            self.topology.options = top
            if self.topology.options:
                self.topology.value = self.topology.options[0]

        except FileNotFoundError:
            pass

    def compute_rmsd(self):
        """Compute RMSD during a trajectory"""
        self.rmsd_button.button_type = "default"
        mask = self.mask.value.replace(
            "protein",
            ":ALA,ARG,ASH,ASN,ASP,CYM,CYS,CYX,GLH,GLN,GLU,GLY,HID,HIE,HIP,HYP,HIS,ILE,LEU,LYN,LYS,MET,PHE,PRO,SER,THR,TRP,TYR,VAL"
        )
        topology = os.path.join(self.md_dir.value, self.topology.value)
        trajectories = [
            os.path.join(self.md_dir.value, f) for f in self.trajectory.value
        ]
        trajectories.sort(key=lambda f: os.path.getmtime(f), reverse=False)
        traj = pt.iterload(trajectories, topology)
        stepsize = get_stepsize(traj)
        frames = list(
            traj.iterframe(step=stepsize,
                           autoimage=True,
                           rmsfit=False,
                           mask=mask))
        log.debug(
            f"Computing RMSD for top {topology} and traj {trajectories} with a step of {stepsize}, using mask {mask}"
        )
        ref = frames[0]
        results = {"Time": [], "RMSD": []}
        with ProcessPoolExecutor(max_workers=max_workers) as ex:
            for rmsd, frame in zip(
                    ex.map(partial(compute_rmsd, ref=ref), frames), frames):
                results["Time"].append(frame.time)
                results["RMSD"].append(rmsd)
        del traj
        self.rmsd_CDS.data = results
        self.rmsd_button.button_type = "primary"

    def autoview_structure(self):
        """Load structure automatically if it has been modified recently"""
        # check if rst7 file was rewritten recently
        update_time = os.path.getmtime(
            os.path.join(self.md_dir.value, self.rst_traj.value))
        # and viewing the latest rst7 file
        if (update_time > self.last_rst_update) and (
                self.rst_traj.value == self.rst_traj.options[0]):
            log.debug(
                f"Updating {self.rst_traj.value} with more recent version: {update_time}"
            )
            self.last_rst_update = update_time
            self.view_structure()
        else:
            log.debug(f"No recent update of restart {self.rst_traj.value}")

    def view_structure(self):
        """Visualize a restart file with NGL"""
        log.debug(
            f"Visualizing top {self.topology.value} and restart {self.rst_traj.value}"
        )
        # load rst7 with pytraj (NGL cannot read it directly)
        traj = pt.load(os.path.join(self.md_dir.value, self.rst_traj.value),
                       os.path.join(self.md_dir.value, self.topology.value))
        traj = pt.autoimage(traj)
        ## pass to parmed to write the pdb data in a StringIO
        # struct = pmd.load_file(os.path.join(self.md_dir.value, self.topology.value), xyz=traj.xyz)
        # f = StringIO()
        # struct.write_pdb(f)
        # pdb_data = f.getvalue().encode("ascii")
        # f.close()
        # write as pdb to temporary file (much faster than parmed + StringIO)
        with NamedTemporaryFile() as f:
            pt.write_traj(f.name, traj, format="pdb", overwrite=True)
            pdb_data = f.read()
        # create javascript code
        with open(os.path.join(self.src_dir, "static", "js",
                               "nglviewer.js")) as f:
            JS_TEMPLATE = f.read()
        self.js_view_structure.code = JS_TEMPLATE % (pdb_data)
        # trigger javascript callback by adding an invisible character to the button label
        self.view_button.label += " "

    def clear_canvas(self):
        """Clear the canvas"""
        log.debug("Clearing canvas")
        self.mdinfo_CDS.data = copy.deepcopy(empty_mddata_dic)

    def read_mdout_header(self, mdout):
        """Read the header of mdout file to search for info on minimization, dt, and output files"""
        log.debug(f"Reading header of {mdout} mdout file")
        mdout_path = os.path.join(self.md_dir.value, mdout)
        found_min, found_dt, found_rst, found_mdcrd = (False, False, False,
                                                       False)
        with open(mdout_path, 'r') as f:
            for i, line in enumerate(f):
                re1 = re.search(r"imin\s*=\s*([01])", line)
                if re1:
                    self.mdout_info[mdout]["min"] = bool(int(re1.group(1)))
                    found_min = True
                re2 = re.search(r"dt\s*=\s*([\.0-9]+)", line)
                if re2:
                    self.mdout_info[mdout]["dt"] = float(re2.group(1))
                    found_dt = True
                re3 = re.search(r"^\| RESTRT: ([^\s]+)\s*$", line)
                if re3:
                    self.mdout_info[mdout]["rst"] = re3.group(1)
                    found_rst = True
                re4 = re.search(r"^\|  MDCRD: ([^\s]+)\s*$", line)
                if re4:
                    self.mdout_info[mdout]["mdcrd"] = re4.group(1)
                    found_mdcrd = True
                if found_min and found_rst and found_mdcrd:
                    if self.mdout_info[mdout][
                            "min"]:  # if min, there's no dt to find
                        log.debug(
                            f"Finished reading header of {mdout}. Closing minimization file."
                        )
                        break
                    else:
                        if found_dt:
                            log.debug(
                                f"Finished reading header of {mdout}. Closing MD file."
                            )
                            break
                elif i > 150:
                    log.debug(
                        f"Could not find all the information within the first 150 lines of {mdout}. Closing file."
                    )
                    break

    def is_min(self, mdout):
        """Returns True if minimization, False if MD, None if the 'imin' keyword was not found"""
        try:
            t = self.mdout_info[mdout]["min"]
        except KeyError:
            log.debug(
                f"Parsing {mdout} mdout file to see if it's a minimization")
            self.read_mdout_header(mdout)
            t = self.mdout_info[mdout].get("min", None)
        log.debug(f"{mdout} is a minimization: {t}")
        return t

    def stream_mdout(self):
        """Parse and stream data from mdout files (minimization or MD simulation)"""
        self.mdout_button.button_type = "default"
        self.clear_canvas()
        mdout = self.mdout_sel.value
        mdout_path = os.path.join(self.md_dir.value, mdout)
        mdout_data = copy.deepcopy(empty_mddata_dic)
        # open file
        with open(mdout_path, 'r') as f:
            log.debug(f"Parsing data from {mdout} mdout file")
            lines = []
            # stop reading when reaching the following lines
            for line in f:
                if ("A V E R A G E S   O V E R" in line) or (
                        "Maximum number of minimization cycles reached"
                        in line):
                    break
                lines.append(line)
        # check if min or md:
        parse_func = parse_min_data if self.is_min(mdout) else parse_md_data
        # parse in parallel
        with ProcessPoolExecutor(max_workers=max_workers) as ex:
            for res in ex.map(parse_func, lines):
                for k, v in res.items():
                    mdout_data[k].extend(v)
        # convert to numpy
        for key, lst in mdout_data.items():
            mdout_data[key] = np.array(lst)
        # stream to CDS
        log.debug(f"Done. Streaming the data from {mdout}")
        self.mdinfo_CDS.stream(mdout_data)
        mdout_data = copy.deepcopy(empty_mddata_dic)
        self.mdout_button.button_type = "primary"

    def latest_mdout_files(self):
        """List all mdout files present in the MD directory, sorted by modification time"""
        mdout_files = [
            f for f in os.listdir(self.md_dir.value)
            if re.search(r'.+\.(md)?out$', f) and ("nohup.out" not in f)
        ]
        mdout_files.sort(
            key=lambda f: os.path.getmtime(os.path.join(self.md_dir.value, f)),
            reverse=True)
        return mdout_files

    def get_mdout_files(self):
        """Update the list of mdout files and automatically select the latest one"""
        log.debug("Updating the list of mdout files")
        self.mdout_files = [None]
        # set mdout file to read
        self.mdout_files = self.latest_mdout_files()
        for mdout in self.mdout_files:
            if not mdout in self.mdout_info:
                self.mdout_info[mdout] = {}
        mdout_options = self.mdout_sel.options
        self.mdout_sel.options = self.mdout_files
        # if new mdout is created
        if len(self.mdout_files) > len(mdout_options):
            self.mdout_sel.value = self.mdout_files[0]

    def parse_mdinfo(self):
        """Parse and stream data read from the mdinfo file"""
        log.debug("Parsing mdinfo file")
        mdinfo_path = os.path.join(self.md_dir.value, "mdinfo")

        try:
            with open(mdinfo_path, 'r') as f:
                lines = f.readlines()
        except FileNotFoundError:
            log.error("No mdinfo file in the current directory")
            return

        mdinfo_data = copy.deepcopy(empty_mddata_dic)
        # min or md
        latest_mdout_file = self.latest_mdout_files()[0]
        parse_func = parse_min_data if self.is_min(
            latest_mdout_file) else parse_md_data

        for i, line in enumerate(lines):
            # data
            res = parse_func(line)
            for k, v in res.items():
                mdinfo_data[k].extend(v)
            # number of steps
            re_steps = re.search(
                r"Total steps :\s*(\d+) \| Completed :\s*(\d+) \| Remaining :\s*(\d+)",
                line)
            if re_steps:
                total = int(re_steps.group(1))
                completed = int(re_steps.group(2))
                remaining = int(re_steps.group(3))
                steps_patch = {
                    "total": [(0, total)],
                    "completed": [(0, completed)],
                    "remaining": [(0, remaining)],
                }
                self.steps_CDS.patch(steps_patch)
                progress = 100 * completed / total
                self.progressbar.title.text = f"Progress: {progress:6.2f}%"
                self.progressbar.x_range.set_from_json("end", total)

            # calculation speed (ns/day)
            re_speed = re.search(r'Average timings for last', line)
            if re_speed:
                re_speed = re.search(r'ns/day =\s*([\.0-9]+)', lines[i + 2])
                speed = float(re_speed.group(1))
                self.calc_speed.text = f"Calculation speed:<br/>{speed} ns/day"

            # time remaining
            re_time = re.search(r'Estimated time remaining:\s*(.+).$', line)
            if re_time:
                time_left = re_time.group(1)
                time_left = pretty_time(time_left)
                self.eta.text = f"Estimated time remaining:<br/>{time_left}"
                break

        # last update
        self.last_update.text = f"Last update:<br/>{time_passed(os.path.getmtime(mdinfo_path))}"
        update_time = os.path.getmtime(mdinfo_path)
        if time.time() - update_time > 5 * 60:  # not updated recently
            self.last_update.style = {
                "font-weight": "bold",
                "color": "#d62727",
                "margin-top": "5px"
            }
        else:
            self.last_update.style = {
                "font-weight": "bold",
                "color": "#444444",
                "margin-top": "5px"
            }

    # only update plots if monitoring the latest mdout file
        if self.mdout_sel.value == latest_mdout_file:
            log.debug(
                f"Currently watching the latest mdout '{self.mdout_sel.value}'"
            )
            # fetch previous stream data as dict
            last_mdinfo_stream = self.mdinfo_CDS.to_df().tail(1).reset_index(
                drop=True).T.to_dict().get(0)
            if last_mdinfo_stream:
                # format the dict
                for key, value in last_mdinfo_stream.items():
                    last_mdinfo_stream[key] = [value]
                # update if mdinfo is different from the previous stream
                if mdinfo_data != last_mdinfo_stream:
                    log.debug("Streaming new data from mdinfo")
                    for key, value in mdinfo_data.items():
                        mdinfo_data[key] = np.array(value)
                    self.mdinfo_CDS.stream(mdinfo_data)
            else:
                log.debug(
                    f"No previous mdinfo data could be retrieved. Streaming new data"
                )
                for key, value in mdinfo_data.items():
                    mdinfo_data[key] = np.array(value)
                self.mdinfo_CDS.stream(mdinfo_data)
        else:
            log.debug(
                f"Currently watching mdout '{self.mdout_sel.value}' != '{latest_mdout_file}'"
            )

    def display_simulations_length(self):
        """Displays simulation length"""
        log.debug("Computing total time of simulation(s) displayed")
        current_time = OrderedDict()
        # discard min files and limit to XX most recent MD files
        self.md_mdout_files = [
            f for f in self.mdout_sel.options if not self.is_min(f)
        ][:self.slider.value]
        for mdout in self.md_mdout_files:
            mdout_path = os.path.join(self.md_dir.value, mdout)
            i = 0
            for line in readlines_reverse(mdout_path):
                i += 1
                re1 = re.search(r"NSTEP =\s*(\d+)", line)
                if re1:
                    current_time[mdout] = int(
                        re1.group(1)) * self.mdout_info[mdout].get(
                            "dt", 0.002) * 1e-3  # in ns
                    break
                if i > 150:
                    break
        data = pd.DataFrame.from_dict(
            current_time, orient="index",
            columns=["time"]).reset_index().rename(columns={"index": "mdout"})
        # compute properties for the pie plot
        data['angle'] = data['time'] / data['time'].sum() * 2 * pi
        # color palette
        data['color'] = sim_palette[:len(current_time)]
        # reverse index order for the barplot
        data = data.reindex(index=data.index[::-1]).reset_index(drop=True)
        data = data.reset_index().rename(columns={"index": "y_coords"})
        # update
        self.dashboard_CDS.data = {k: data[k].tolist() for k in data.columns}
        total_time = data["time"].sum()
        self.pie.title.text = f"Simulations length: {total_time:.2f} ns"

    def update_dashboard(self):
        log.debug("Starting update of the dashboard")
        self.get_mdout_files()
        self.parse_mdinfo()
        self.display_simulations_length()
        self.autoview_structure()
        log.debug("Finished updating the dashboard")

    def callback_slider(self, attr, old, new):
        log.debug(f"Slider update detected: from {old} to {new}")
        self.display_simulations_length()

    def add_callbacks(self):
        log.debug("Adding callbacks to the widgets")
        # User input
        self.md_dir.on_change("value_input", self.autocomp_callback)
        self.md_dir.on_change("value", self.traj_top_callback)
        # RMSD
        self.rmsd_button.on_click(self.compute_rmsd)
        # NGLView
        self.js_view_structure = CustomJS(code="",
                                          args={
                                              "ligand_mask": self.ngl_lig,
                                              "repr": self.ngl_representations
                                          })
        self.ngl_help_button.on_click(self.ngl_help)
        # hack to execute both python and JS code on button click
        self.view_button.js_on_change("label", self.js_view_structure)
        self.view_button.on_click(self.view_structure)
        # MDout parsing
        self.mdout_button.on_click(self.stream_mdout)
        self.slider.on_change("value_throttled", self.callback_slider)
Ejemplo n.º 14
0
def clear_plot():
    source_data.data = dict(x=[], y=[])
    pred_data.data = dict(x=[], y=[])
    source_bars.data = dict(y=[], right=[])
    pred_line.data = dict(y=[], x=[])
    tsne.data = dict(y=[], x=[])


def update_plot_signature(attr, old, new):
    # print(attr, old, new)
    update_plot()


# Disable controls initially
change_model()

plot_run.on_click(update_plot)
plot_clear.on_click(clear_plot)
ctl_model.on_click(change_model)
ctl_feat_reduce.on_click(update_plot)
ctl_kernel.on_click(update_plot)


# Page Layout
col_inputs = column(plot_ctls, ctl_inputs, disp_disclaimer)
row_plots = row(plot_mood_scatter, plot_mood_bar)
row_page = row(col_inputs, column(row_plots, plot_feature_scatter), column(disp_features, disp_score), width=1200)
curdoc().add_root(row_page)
curdoc().title = "Daylio Data Display"
Ejemplo n.º 15
0
class signalHandler:
    def __init__(self,
                 signalFilename="",
                 lyricsFilename="",
                 signal=[],
                 sampleRate=0,
                 color="red"):
        self.path = os.getcwd()

        logger.logData(source="Signal handler",
                       priority="INFO",
                       msgType="Setup start",
                       msgData=())

        self.setupVariables()

        self.color = color
        self.setupMapper()
        self.setupColorBar()

        self.lyricsImported = 0
        if lyricsFilename:
            self.setupLyricTable(lyricsFilename)
            self.lyricsImported = 1

        self.setupPlotWindow()
        self.setupControls()
        self.setupGUI()

        if signalFilename:
            self.importFile(signalFilename)

        if len(signal):
            self.addSignal(signal, sampleRate)

        logger.logData(source="Signal handler",
                       priority="INFO",
                       msgType="Setup done",
                       msgData=((self.signalImported)))

    def setupGUI(self):
        """Wraps the plot, slider, and tool column into one layout"""

        audioCol = column(self.p,
                          self.colorBar,
                          self.timeWindowSlider,
                          height=self.figureHeight)
        # dtgCol = column(self.dtg.gui,width=400)
        if self.lyricsImported:
            # self.gui = self.lyricTable
            self.gui = row(self.lyricsGui,
                           audioCol,
                           self.controls,
                           height=self.figureHeight - 110)
        else:
            self.gui = row(audioCol,
                           self.controls,
                           height=self.figureHeight - 110)

    def setupVariables(self):
        """sets up important variables to the tool, including
			figure size
			dummy variables to use before file import
			tool options - loop, button sizes, button delay
		"""

        try:
            self.masterPath = os.getcwd()  #use when running from Sublime
            os.listdir(self.masterPath)

        except:
            self.masterPath = os.path.join("soundTools",
                                           "")  #use when running from Bokeh
            os.listdir(self.masterPath)

        self.subtitlePath = os.path.join(self.masterPath, "lyrics")
        self.audioPath = os.path.join(self.masterPath, "audio")
        self.webpagePath = os.path.join(self.masterPath, "webpages")

        self.figureWidth = 1000
        self.figureHeight = 500

        self.buttonWidth = 200
        self.buttonHeight = 15
        self.numXTicks = 4
        self.soundPlaying = 0
        self.signalImported = 0
        self.lastChunkIndex = 0

        self.strideMultiplier = 100

        self.sweepStartSample = 0
        self.sweepEndSample = 10
        self.activeChannels = []
        self.yRange = (-1, 1)
        self.channelAnchorYs = np.arange(0, self.yRange[1], 2)

        self.plotStyle = 0
        self.glyphsSetup = 0
        self.loop = 0
        self.plotMode = 0

        self.outputFilename = "output"
        self.updateDelay = 12  #delay period in milliseconds befwen timeline update while playing the active signal
        self.windowChunkLength = 10  #seconds

    def showGui(self):
        output_file(os.path.join("webpages", "signalHandler.html"))

        logger.logData(source="Signal handler",
                       priority="INFO",
                       msgType="Show",
                       msgData=())

        show(self.gui)
        curdoc().add_root(self.gui)

    def addSignal(self, signalIn, sampleRate):
        """Adds a prexisting list/mutliDim ndarray  (with known sample rate) as the active signal and updates plot"""

        #keep the original in case of a reset
        self.originalSignal = signalIn

        #signal gets changed on update of time slider
        self.signal = self.originalSignal
        #activeSignal gets changed on update of of signal AND update of active channels
        self.activeSignal = self.signal

        self.sampleRate = sampleRate
        self.signalImported = 1

        logger.logData(source="Signal handler",
                       priority="INFO",
                       msgType="Signal add",
                       msgData=())

        self.analyzeSignal()

    def importFile(self, filename):
        """imports a wav file into the tool and updates the plot and tools"""

        #check to make sure the filename is valid
        try:
            self.sampleRate, self.originalSignal = wavfile.read(
                os.path.join(
                    self.audioPath,
                    filename))  #keep the original for master reference
        except:
            logger.logData(source="Signal handler",
                           priority="WARN",
                           msgType="Import file fail",
                           msgData=(filename))
            return
        #update tool's internal filename
        self.filename = filename
        #import the wav file
        self.signal = self.originalSignal
        self.activeSignal = self.signal
        self.signalImported = 1

        logger.logData(source="Signal handler",
                       priority="INFO",
                       msgType="Import file",
                       msgData=(filename))

        #get relevant signal info and update plot
        self.analyzeSignal()

    def analyzeSignal(self):
        """Parses the metadata from the active signal and updates plot and tools"""

        self.glyphsSetup = 0
        #get number of channels of signal
        try:
            self.numChannels = self.signal.shape[1]
        except:
            #if it's single channel its imported as a list, make it a 1D ndarray
            self.numChannels = 1
            self.signal = np.transpose(np.array([self.signal]))

        self.activeChannels = list(range(self.numChannels))
        self.channelButtons.labels = list(map(str, self.activeChannels))

        self.channelButtons.active = self.activeChannels

        if not np.any(self.signal):

            logger.logData(source="Signal handler",
                           priority="WARN",
                           msgType="Empty",
                           msgData=())

            return

        self.numSamples = len(self.signal)
        self.sampleIndices = range(self.numSamples)
        self.messedUpTs = self.sampleIndices

        self.updateMapperHigh(self.numSamples)
        self.updateColorBar(self.sampleIndices)

        self.signalDuration = self.numSamples / float(self.sampleRate)
        self.windowChunks = int(
            (self.signalDuration / self.windowChunkLength)) + 1

        #update the time slider with the imported signal's duration
        self.timeWindowSlider.end = self.signalDuration
        self.timeWindowSlider.value = [0, self.signalDuration]

        self.sweepStartSample = 0
        self.sweepEndSample = self.numSamples

        #setup the ticker to replace indices with timestamps
        self.setMasterXAxisTicker()

        #PLOT SCATTER PARAMS
        #get max amplitude of signal to adjust y range and channel spacing
        self.sigPeak = np.amax(self.signal)
        self.yRange = (0, 2 * self.numChannels * self.sigPeak)

        #generate offsets to space multiple channels out in the y axis
        self.channelAnchorYs = np.arange(self.sigPeak, self.yRange[1],
                                         2 * self.sigPeak)

        logger.logData(source="Signal handler",
                       priority="INFO",
                       msgType="Analyze",
                       msgData=(round(self.signalDuration,
                                      3), self.numChannels, self.numSamples,
                                self.sampleRate))

        self.drawActivePlot()
        # self.drawFullSignal()

    def setMasterXAxisTicker(self):
        #ticker dictionary to rpaplce x axis index ticks with their coirrosponding timestamps
        self.masterTicker = list(
            range(0, self.numSamples, int(self.numSamples / self.numXTicks)))
        self.masterXAxisOverrideDict = {}

        timeLabels = np.linspace(0, self.signalDuration, self.numXTicks)
        for sampleInd, timeLabel in zip(self.masterTicker, timeLabels):
            self.masterXAxisOverrideDict[sampleInd] = str(round(timeLabel, 3))

        #set up the size and duration of the sub-chunks displayed while the signal is playing
        self.samplesPerChunk = int(self.numSamples / self.windowChunks)

        self.chunkDuration = self.samplesPerChunk / float(self.sampleRate)
        #the absolute index values comprising the x axis ticks
        self.chunkTicker = list(
            range(0, self.samplesPerChunk,
                  int(self.samplesPerChunk / self.numXTicks)))

        # self.chunkLabels = np.linspace(0,self.chunkDuration,10)
        # self.chunkTickOverride = {}
        # for sampleInd,timeLabel in zip(self.chunkTicker,self.chunkLabels):
        # 	self.chunkTickOverride[sampleInd] = str(round(timeLabel,3))

    def fileCallback(self, attr, old, new):
        """Callback assigned to choosing a file from the file browser"""

        filename = new['file_name'][0]
        self.importFile(filename)

    def fileButtonSetup(self):
        """Creates a "File opener" button and assigns a javascript callback to it that opens an os-independent file picker window
		imports chosen file into the class"""
        fileSource = ColumnDataSource({'file_name': []})

        self.fileImportButton.callback = CustomJS(
            args=dict(file_source=fileSource),
            code="""
		function read_file(filename) {
		    var reader = new FileReader();
		    reader.onload = load_handler;
		    reader.onerror = error_handler;
		    // readAsDataURL represents the file's data as a base64 encoded string
		    reader.readAsDataURL(filename);
		}

		function load_handler(event) {
		    file_source.data = {'file_name':[input.files[0].name]};
		    file_source.trigger("change");
		}

		function error_handler(evt) {
		    if(evt.target.error.name == "NotReadableError") {
		        alert("Can't read file!");
		    }
		}

		var input = document.createElement('input');
		input.setAttribute('type', 'file');
		input.onchange = function(){
		    if (window.FileReader) {
		        read_file(input.files[0]);
		    } else {
		        alert('FileReader is not supported in this browser');
		    }
		}
		input.click();
		""")

        fileSource.on_change('data', self.fileCallback)

    def setupControls(self):
        """Called on setup, creates buttons and sliders to:
			open a local audio file
			set loop mode
			update the active timespan
			play the active timespan
			set filename to save active signal to
			save active signal to that filename
		"""

        #check boxes to choose what plots to display
        self.plotModeButtons = RadioButtonGroup(
            labels=["Wav", "FFT", "Spectrogram"],
            active=self.plotMode,
            button_type="warning",
            width=self.buttonWidth,
            height=self.buttonHeight)
        self.plotModeButtons.on_change("active", self.plotModeCallback)

        #choose betwen line or scatter plot
        self.plotStyleButtons = RadioButtonGroup(labels=["Line", "Scatter"],
                                                 active=0,
                                                 button_type="danger",
                                                 width=self.buttonWidth,
                                                 height=self.buttonHeight)
        self.plotStyleButtons.on_change("active", self.plotStyleCallback)

        channelTitle = Div(text="""<b>Audio Channels:</b>""",
                           width=self.buttonWidth,
                           height=2)
        self.channelButtons = CheckboxButtonGroup(labels=["-"],
                                                  active=[0],
                                                  button_type="primary",
                                                  width=self.buttonWidth,
                                                  height=self.buttonHeight)
        self.channelButtonRow = column(channelTitle,
                                       self.channelButtons,
                                       width=self.buttonWidth,
                                       height=self.buttonHeight * 2)
        self.channelButtons.on_change("active", self.channelButtonCallback)

        #creates a filebutton and assigns it a callback linked to a broser-based file browser
        self.fileImportButton = Button(label="Import File",
                                       button_type="success",
                                       width=self.buttonWidth,
                                       height=self.buttonHeight)
        self.fileButtonSetup()

        #create a loop toggle button and assigns a callback to it
        self.loopAudioToggle = Toggle(label="Loop",
                                      button_type="success",
                                      width=self.buttonWidth,
                                      height=self.buttonHeight)
        self.loopAudioToggle.on_click(self.loopAudioCallback)

        #double ended slider to clip audio by time
        self.timeWindowSlider = RangeSlider(start=0,
                                            end=1,
                                            value=[0, 1],
                                            step=.05,
                                            title="Wav File Window",
                                            width=self.figureWidth,
                                            height=self.buttonHeight)
        self.timeWindowSlider.on_change("value", self.timeSliderCallback)

        #button to commit clip changes to active signal
        self.updateButton = Button(label="Update",
                                   button_type="success",
                                   width=self.buttonWidth,
                                   height=self.buttonHeight)
        self.updateButton.on_click(self.updateButtonCallback)

        #button to play active signal,
        self.playButton = Button(label="Play",
                                 button_type="success",
                                 width=self.buttonWidth,
                                 height=self.buttonHeight)
        self.playButton.on_click(self.playSound)

        self.filenameBox = TextInput(value="output",
                                     title="Output Filename:",
                                     width=self.buttonWidth)

        #button to write active signal to file
        self.writeFileButton = Button(label="Write Active File",
                                      button_type="success",
                                      width=self.buttonWidth,
                                      height=self.buttonHeight)
        self.writeFileButton.on_click(self.writeFileButtonCallback)

        #button to reset tool to state right after signal import
        self.resetButton = Button(label="Reset",
                                  button_type="success",
                                  width=self.buttonWidth,
                                  height=self.buttonHeight)
        self.resetButton.on_click(self.resetButtonCallback)

        self.resetZoomButton = Button(label="Reset Zoom",
                                      button_type="success",
                                      width=self.buttonWidth,
                                      height=self.buttonHeight)
        self.resetZoomButton.js_on_click(
            CustomJS(args=dict(p=self.p), code="""
    		p.reset.emit()
			"""))

        self.generalControlsColumn = column(self.plotModeButtons,
                                            self.plotStyleButtons,
                                            self.filenameBox,
                                            self.channelButtonRow,
                                            width=self.buttonWidth)

        self.buttonColumn = column(
            self.resetZoomButton,
            self.fileImportButton,
            self.updateButton,
            self.loopAudioToggle,
            self.playButton,
            self.writeFileButton,
            self.resetButton,
            width=self.buttonWidth)  #,height=self.figureHeight)

        self.controls = row(self.generalControlsColumn, self.buttonColumn)
        #wrap buttons and text box in a column of fixed width
        # self.buttonColumn = column(self.plotModeButtons,self.plotStyleButtons,self.channelButtonRow,self.resetZoomButton,self.fileImportButton,self.updateButton,self.loopToggle,self.playButton,self.writeFileButton,self.resetButton,self.filenameBox,width=self.buttonWidth)#,height=self.figureHeight)

    #choose active channels
    def channelButtonCallback(self, attr, old, new):
        if not self.signalImported: return

        try:
            self.activeChannels = new
            self.activeSignal = self.signal[:, self.activeChannels]
            self.glyphsSetup = 0
            self.drawActivePlot()

            logger.logData(source="Signal handler",
                           priority="INFO",
                           msgType="Channel update",
                           msgData=((old, new)))

        except:
            logger.logData(source="Signal handler",
                           priority="WARN",
                           msgType="Channel fail",
                           msgData=(old))

            return

        #choose between line or scatter plot
    def plotStyleCallback(self, attr, old, new):
        self.plotStyle = new
        self.glyphsSetup = 0

        # self.drawFullSignal()
        self.drawActivePlot()

    def plotModeCallback(self, att, old, new):
        self.plotMode = new
        self.drawActivePlot()

    def loopAudioCallback(self, event):
        """Called on toggling of the loop button,
			binary inverts previous loop val"""

        self.loop = 1 - self.loop

    def writeFileButtonCallback(self):
        """Called on click of the write Fiile button
		Writes the active signal to the filename set by the textbox"""
        outputFilename = self.filenameBox.value + ".wav"
        outputPath = os.path.join(self.path, "audio", outputFilename)

        numChannels = len(self.activeChannels)

        logger.logData(source="Signal handler",
                       priority="INFO",
                       msgType="Write",
                       msgData=(outputFilename, numChannels, self.sampleRate,
                                self.signalDuration))

        wavfile.write(outputPath, self.sampleRate, self.activeSignal)

    # def resetZoomCallback(self):
    # 	print(1)

    def resetButtonCallback(self):
        """Returns the tool to state it was immediately after file was imported"""

        #if no signal is imported, do nothing
        if not self.signalImported: return

        #reset active to signal to the original, unclipped signal
        self.signal = self.originalSignal

        logger.logData(source="Signal handler",
                       priority="INFO",
                       msgType="Reset",
                       msgData=())

        #return variables and plot to original state
        self.analyzeSignal()

    def updateButtonCallback(self):
        """Called on press of the update button,
			clips the signsal by the estart and end trimes decreed by the time slider,
			resets the plot to like the clipped signal is the new full signal"""

        #if no signal is imported, do nothing
        if not self.signalImported:
            logger.logData(source="Signal handler",
                           priority="WARN",
                           msgType="Update failed",
                           msgData=())

            return

        #clip all channel samples corresponding to the times on slider
        self.signal = self.signal[self.sweepStartSample:self.sweepEndSample, :]

        logger.logData(source="Signal handler",
                       priority="INFO",
                       msgType="Update signal",
                       msgData=())

        #update variables and plot with clipped signal
        self.analyzeSignal()

    def timeSliderCallback(self, attr, old, new):
        """Called on update of the time slider
			moves the sweep start/end lines used for clipping the signal when the update button is pressed"""

        if not self.signalImported:
            return

        try:
            #convert the start and end times to integer sample numbers and update internal locations
            self.sweepStartSample = int(new[0] * self.sampleRate)
            self.sweepEndSample = int(new[1] * self.sampleRate)

            #update sweep line graphics
            startLine = self.p.select_one({'name': 'sweepStartLine'})
            startLine.data_source.data = {
                'x': [self.sweepStartSample, self.sweepStartSample],
                'y': self.yRange
            }

            endLine = self.p.select_one({'name': 'sweepEndLine'})
            endLine.data_source.data = {
                'x': [self.sweepEndSample, self.sweepEndSample],
                'y': self.yRange
            }
        except:
            return

    def shiftSamples(self, channelIndex):
        """Element wise adds to the channel's vector to offset a channel so it can vbe plotted alongside other channels"""

        channelSamples = self.signal[:, channelIndex]

        reducedSamples = channelSamples[::self.strideMultiplier]
        return reducedSamples + self.channelAnchorYs[channelIndex]

    def setupPlotWindow(self):
        """Creates a window containing the channel plots"""

        p = figure(height=300,
                   width=self.figureWidth,
                   x_range=(0, 1),
                   y_range=(0, 1),
                   tools="box_zoom",
                   toolbar_location=None,
                   output_backend="webgl")
        # p.toolbar.active_scroll = "auto"
        p.yaxis.visible = False
        p.grid.visible = False
        self.p = p

    def updateFigureForSpectrogram(self):
        self.p.x_range.end = self.numSamples
        self.p.y_range.end = self.yRange[1]

        self.p.xaxis.ticker = self.masterTicker
        self.p.xaxis.major_label_overrides = self.masterXAxisOverrideDict

    def plotSpectrogram(self):
        """Plots a log spectrogram of the active audio, returns figure object"""

        #max freq represetnedf (nyquist constrained)
        imgHeight = self.sampleRate / 2

        self.p.y_range.end = imgHeight * self.numChannels

        imgWidth = self.signalDuration
        self.p.x_range.end = imgWidth

        for channelNum in self.activeChannels:
            channelSignal = self.signal[:, channelNum]

            freqs, times, data = self.log_specgram(channelSignal,
                                                   self.sampleRate)

            self.p.image(image=[data],
                         x=0,
                         y=imgHeight * channelNum,
                         dw=imgWidth,
                         dh=imgHeight,
                         palette="Spectral11")

    def log_specgram(self,
                     audio,
                     sampleRate,
                     window_size=20,
                     step_size=10,
                     eps=1e-10):
        """Kraggin log spectrogram useful for MFCC analysis"""

        nperseg = int(round(window_size * sampleRate / 1e3))
        noverlap = int(round(step_size * sampleRate / 1e3))
        freqs, times, spec = spectrogram(audio,
                                         fs=sampleRate,
                                         window='hann',
                                         nperseg=nperseg,
                                         noverlap=noverlap,
                                         detrend=False)
        return freqs, times, np.log(spec.T.astype(np.float32) + eps)

    def setupPlotScatterGlyphs(self):
        self.p.line([self.sweepStartSample, self.sweepStartSample],
                    self.yRange,
                    color="blue",
                    line_width=2,
                    name="sweepStartLine")
        self.p.line([self.sweepEndSample, self.sweepEndSample],
                    self.yRange,
                    color="blue",
                    line_width=2,
                    name="sweepEndLine")

        self.p.line([0, 0],
                    self.yRange,
                    color='red',
                    line_width=2,
                    name='timeLine')

        # self.scatterSource = {"x":[],"place":[]}

        self.scatterSources = []
        for channelNum in self.activeChannels:
            self.scatterSources.append(
                ColumnDataSource({
                    "x": list(self.sampleIndices),
                    "y": list(self.sampleIndices),
                    "place": self.messedUpTs
                }))
            # self.p.scatter(x=[],y=[],radius=.1, fill_color={'field':"place",'transform': self.mapper},name="audioLine" + str(channelNum))
            self.p.scatter(x="x",
                           y="y",
                           radius=1,
                           source=self.scatterSources[channelNum],
                           fill_color={
                               'field': "place",
                               'transform': self.mapper
                           },
                           line_color={
                               'field': "place",
                               'transform': self.mapper
                           },
                           name="audioLine" + str(channelNum))

    def setupLinePlotGlyphs(self):
        self.p.line([self.sweepStartSample, self.sweepStartSample],
                    self.yRange,
                    color="blue",
                    line_width=2,
                    name="sweepStartLine")
        self.p.line([self.sweepEndSample, self.sweepEndSample],
                    self.yRange,
                    color="blue",
                    line_width=2,
                    name="sweepEndLine")

        self.p.line([0, 0],
                    self.yRange,
                    color='red',
                    line_width=2,
                    name='timeLine')
        for channelNum in self.activeChannels:
            self.p.line(x=[],
                        y=[],
                        line_width=.3,
                        color=self.color,
                        name="audioLine" + str(channelNum))

    def drawActivePlot(self):
        if not self.signalImported: return

        if self.plotMode == 0:
            self.drawFullSignal()
        elif self.plotMode == 1:
            self.getFFT()
        else:
            self.plotSpectrogram()

    def drawFullSignal(self):
        if self.glyphsSetup == 0:
            self.p.renderers = []

            if self.plotStyle:
                self.setupPlotScatterGlyphs()
            else:
                self.setupLinePlotGlyphs()

            self.glyphsSetup = 1
        """redraws each channel of the full plot and updates the xaxis to the full signal duration"""
        for channelNum in self.activeChannels:

            shiftedSamples = self.shiftSamples(channelNum)

            reducedSampleIndices = self.sampleIndices[::self.strideMultiplier]

            if self.plotStyle:
                self.scatterSources[channelNum].data = {
                    'x': reducedSampleIndices,
                    'y': list(shiftedSamples),
                    "place": reducedSampleIndices
                }
            else:
                channelLine = self.p.select_one(
                    {'name': 'audioLine' + str(channelNum)})
                channelLine.data_source.data = {
                    'x': reducedSampleIndices,
                    'y': shiftedSamples,
                    "place": reducedSampleIndices
                }
        #update x axis with full timespan
        self.p.x_range.end = self.numSamples
        self.p.y_range.end = self.yRange[1]

        self.p.xaxis.ticker = self.masterTicker
        self.p.xaxis.major_label_overrides = self.masterXAxisOverrideDict

    def playSound(self):
        """Starts playing the signal, and draws a sweeping vertical line on actively updating sub-samples of the audfio"""

        #if the "Play" button is pushed during play, it acts as a stop button
        if self.soundPlaying == 1:
            logger.logData(source="Signal handler",
                           priority="INFO",
                           msgType="Pause",
                           msgData=())

            self.stopAudio()
            return

        #if no signal is imported, do nothing
        if not self.signalImported: return

        #hide sweep lines until their chunk occurs
        startLine = self.p.select_one({'name': 'sweepStartLine'})
        startLine.visible = False

        endLine = self.p.select_one({'name': 'sweepEndLine'})
        endLine.visible = False

        ##Chunk-specific sweep lines
        self.startLineAdded = 0
        self.endLineAdded = 0

        #precompute which chunk the sweep lines are in for speed
        self.sweepStartChunk = int(
            np.floor(self.sweepStartSample / (self.samplesPerChunk + 1)))
        self.sweepEndChunk = int(
            np.floor(self.sweepEndSample / (self.samplesPerChunk + 1)))

        #precompute their indices in their chunk
        self.shiftedSweepStart = self.sweepStartSample - self.sweepStartChunk * self.samplesPerChunk
        self.shiftedSweepEnd = self.sweepEndSample - self.sweepEndChunk * self.samplesPerChunk

        if self.p.select_one({'name': 'sweepEndLineChunk'}) == None:
            #preadd the lines for speed
            self.p.line([self.shiftedSweepStart, self.shiftedSweepStart],
                        self.yRange,
                        color="blue",
                        line_width=2,
                        visible=False,
                        name="sweepStartLineChunk")
            self.p.line([self.shiftedSweepEnd, self.shiftedSweepEnd],
                        self.yRange,
                        color="blue",
                        line_width=2,
                        visible=False,
                        name="sweepEndLineChunk")

        #update the x axis with the sub-chunk values
        self.p.x_range.end = self.samplesPerChunk
        self.p.xaxis.ticker = self.chunkTicker
        self.p.xaxis.major_label_overrides = self.createChunkXAxisOverrideDict(
            0)

        #set the play button to read "Pause" to pull double duty
        self.playButton.label = "Pause"

        logger.logData(source="Signal handler",
                       priority="INFO",
                       msgType="Play",
                       msgData=())

        #log start time to keep track of where the time line should be
        self.startTime = time.time()

        #start playing the sound
        try:
            sd.play(self.activeSignal,
                    self.sampleRate,
                    loop=self.loop,
                    blocking=False)
        except:
            logger.logData(source="Signal handler",
                           priority="CRIT",
                           msgType="Play failed",
                           msgData=())
            self.playButton.label = "Play"
            return
        self.soundPlaying = 1

        #add a call callback to trigger periodcially and update the timeline and sub-samples
        self.perCallback = curdoc().add_periodic_callback(
            self.update, self.updateDelay)

    def createChunkXAxisOverrideDict(self, chunkIndex):
        """	creates a dictionary replacing absolute index ticks on the x axis with their corrosponding times
		"""
        #get the time labels corrosponding to this chunk
        chunkTimeLabels = np.linspace(self.chunkDuration * chunkIndex,
                                      self.chunkDuration * (chunkIndex + 1),
                                      self.numXTicks)

        chunkTickOverride = {}
        for sampleInd, timeLabel in zip(self.chunkTicker, chunkTimeLabels):
            #replace each sample index x tick with the time label
            chunkTickOverride[sampleInd] = str(round(timeLabel, 3))
        return chunkTickOverride

    def update(self):
        """Set to be called periodically when audio is playing to draw the active time line on the audio signal"""

        if self.loop:
            #mod the time played by total signal duration to keep the time line accurate for multiple plays
            deltaTime = (
                time.time() - self.startTime
            ) % self.signalDuration  #get time elapsed since the file started playing
        else:
            deltaTime = time.time(
            ) - self.startTime  #get time elapsed since the file started playing

        #if signal not done playing
        if deltaTime < self.signalDuration:
            #number of samples elapsed
            dSamples = deltaTime * self.sampleRate

            #get the active chunk
            chunkIndex = int(self.windowChunks * (dSamples / self.numSamples))

            #if the chunk is different, need to update the audio plot window to the next chunk
            if self.lastChunkIndex != chunkIndex:
                #get the starting and ending sample indices for the next chunk
                chunkStartIndex = self.samplesPerChunk * chunkIndex
                chunkEndIndex = self.samplesPerChunk * (chunkIndex + 1)

                #check if any of the sweep lines lie in this chunk
                if self.startLineAdded:
                    self.p.select_one({
                        'name': 'sweepStartLineChunk'
                    }).visible = False
                    self.startLineAdded = 0

                if chunkIndex == self.sweepStartChunk:
                    self.p.select_one({
                        'name': 'sweepStartLineChunk'
                    }).visible = True
                    self.startLineAdded = 1

                if self.endLineAdded:
                    self.p.select_one({
                        'name': 'sweepEndLineChunk'
                    }).visible = False
                    self.endLineAdded = 0

                if chunkIndex == self.sweepEndChunk:
                    self.p.select_one({
                        'name': 'sweepEndLineChunk'
                    }).visible = True
                    self.endLineAdded = 1

                #get the signal samples from this chunk and downsample them and shift them by channel
                reducedChunkSamps = self.signal[
                    chunkStartIndex:chunkEndIndex:self.
                    strideMultiplier] + self.channelAnchorYs

                reducedPlaces = list(
                    range(chunkStartIndex, chunkEndIndex,
                          self.strideMultiplier))
                #original
                # chunkSamps = self.signal[chunkStartIndex:chunkEndIndex]
                # shiftedChunkSamps = chunkSamps + self.channelAnchorYs

                reducedSampleIndices = list(
                    range(0, self.samplesPerChunk, self.strideMultiplier))

                #update plot for each channel
                for channelIndex in self.activeChannels:
                    if self.plotMode == 0:
                        audioLine = self.p.select_one(
                            {'name': "audioLine" + str(channelIndex)})
                        audioLine.data_source.data = {
                            'x': reducedSampleIndices,
                            'y': reducedChunkSamps[:, channelIndex],
                            "place": reducedPlaces
                        }

                        # audioLine.data_source.data = {'x': reducedSampleIndices, 'y': shiftedChunkSamps[:,channelIndex],"place":reducedPlaces}
                    else:
                        self.scatterSources[channelIndex].data = {
                            "x": reducedSampleIndices,
                            "y": self.sampleIndices,
                            "place": self.messedUpTs
                        }

                #update the x-axis ticks with the new times
                self.p.xaxis.major_label_overrides = self.createChunkXAxisOverrideDict(
                    chunkIndex)

                #update chunk index with new one
                self.lastChunkIndex = chunkIndex

            ##time line update
            #get the glyph for the time line
            timeLine = self.p.select_one({'name': 'timeLine'})

            #sample index of the timeline is total samples elapsed less the number of samples in all previous chunks
            timeLineIndex = dSamples - chunkIndex * self.samplesPerChunk

            #update the time line with the new times
            timeLine.data_source.data = {
                'x': [timeLineIndex, timeLineIndex],
                'y': self.yRange
            }

        #signal IS done playing
        else:
            if self.loop:
                return
            else:
                self.stopAudio()

    def stopAudio(self):
        """Stops the audio playing, returns the plot to state before audio started playing"""
        #stop the updating of the time line
        curdoc().remove_periodic_callback(self.perCallback)

        #stop playing the signal
        sd.stop()
        self.soundPlaying = 0

        #change play button back to play from pause
        self.playButton.label = "Play"

        logger.logData(source="Signal handler",
                       priority="INFO",
                       msgType="Play done",
                       msgData=())

        #restore plot to full signal
        self.drawActivePlot()

        #redraw sweep lines on the full signal plot
        startLine = self.p.select_one({'name': 'sweepStartLine'})
        startLine.visible = True

        endLine = self.p.select_one({'name': 'sweepEndLine'})
        endLine.visible = True

        #return time line to t=0
        timeLine = self.p.select_one({'name': 'timeLine'})
        timeLine.data_source.data["x"] = [0, 0]

    def setupMapper(self):
        self.mapper = LinearColorMapper(palette="Inferno256", low=0, high=10)

    def updateMapperPalette(self, newColors):
        self.mapper.palette = newColors

    def updateMapperHigh(self, newHigh):
        self.mapper.high = newHigh

    def updateColorBar(self, times):
        colorBarPlot = self.gui.select_one({'name': 'colorBarPlot'})
        colorBarPlot.x_range.end = self.numSamples
        colorBar = self.gui.select_one({'name': 'colorBar'})

        self.messedUpTs = times
        self.colorSource.data = {
            "x": self.sampleIndices,
            "place": self.messedUpTs
        }

        # if self.plotMode == 1:
        # 	for channelInd in self.activeChannels:
        # 		self.scatterSources[channelIndex].data = {"x":self.sampleIndices,"y":self.sampleIndices,"place":self.messedUpTs}

    def setupColorBar(self):
        colorTimeline = figure(height=30,
                               y_range=(-.5, .5),
                               width=self.figureWidth,
                               x_range=(0, 10),
                               toolbar_location=None,
                               output_backend="webgl",
                               name="colorBarPlot",
                               tools="")

        colorTimeline.axis.visible = False
        colorTimeline.grid.visible = False

        # colorTimeline.image(image=range(self.numSamples),x=0,y=.5,dh=1,dw=1,fill_color={'field':"x",'transform': self.mappers[colorBarType-1]},
        #        name="cbar" + str(colorBarType))

        self.colorSource = ColumnDataSource({
            "x": range(10),
            "place": range(10)
        })

        # colorTimeline.rect(x="x", y=0, width=1, height=1,fill_color={'field':"place",'transform': self.mapper},name="colorBar",
        #        line_width=0.0,line_color= None,line_alpha = 0.0,source=colorSource
        #        )

        colorBar = Rect(x="x",
                        y=0,
                        width=1,
                        height=1,
                        fill_color={
                            'field': "place",
                            'transform': self.mapper
                        },
                        name="colorBar",
                        line_width=0.0,
                        line_color=None,
                        line_alpha=0.0)

        colorTimeline.add_glyph(self.colorSource, colorBar)

        self.colorBar = colorTimeline

    def getFFT(self):
        """Plots the fast fourier transform of the active audio, returns a figure object"""
        fftHeight = self.numChannels
        self.p.y_range.end = fftHeight

        maxFreq = self.sampleRate / 2
        self.p.x_range.end = maxFreq

        for channelNum in self.activeChannels:
            sigPadded = self.signal[:, channelNum]

            # Determine frequencies
            f = np.fft.fftfreq(self.numSamples) * self.sampleRate

            #pull out only positive frequencies (upper half)
            upperHalf = int(len(f) / 2)
            fHalf = f[:upperHalf]

            # Compute power spectral density
            psd = np.abs(np.fft.fft(sigPadded))**2 / self.numSamples

            #pull out only power densities for the positive frequencies
            psdHalf = psd[:upperHalf]

            #nromalize y vals
            psdHalf = psdHalf / max(psdHalf)

            #shift them to allow multiple channels
            psdHalf += channelNum

            self.p.line(fHalf, psdHalf)

    def lyricModeCallback(self, event):
        self.lyricMode = 1 - self.lyricMode

        if self.lyricMode:
            self.lyricsHandler.lyricModeButton.label = "Change to start lyric"
        else:
            self.lyricsHandler.lyricModeButton.label = "Change to end lyric"

    def dataTableCallback(self, att, old, new):
        if not self.activeChannels:
            logger.logData(source="Signal handler",
                           priority="WARN",
                           msgType="Lyric empty",
                           msgData=())
            return

        selectionIndex = self.lyricsHandler.lyricTableHandler.source.selected.indices[
            0]

        timestamp = self.lyricsHandler.lyricTableHandler.source.data[
            "timestamps"][selectionIndex]
        lyricText = self.lyricsHandler.lyricTableHandler.source.data["lyrics"][
            selectionIndex]

        timestampSeconds = timestamp.seconds
        lyricSample = int(timestampSeconds * self.sampleRate)

        if self.lyricMode == 0:
            #update sweep line graphics
            self.sweepStartSample = lyricSample
            startLine = self.p.select_one({'name': 'sweepStartLine'})
            startLine.data_source.data = {
                'x': [lyricSample, lyricSample],
                'y': self.yRange
            }

            logger.logData(source="Lyrics",
                           priority="INFO",
                           msgType="Start lyric",
                           msgData=(timestamp, lyricText))

        else:
            self.sweepEndSample = lyricSample
            endLine = self.p.select_one({'name': 'sweepEndLine'})
            endLine.data_source.data = {
                'x': [lyricSample, lyricSample],
                'y': self.yRange
            }
            logger.logData(source="Lyrics",
                           priority="INFO",
                           msgType="End lyric",
                           msgData=(timestamp, lyricText))

    def setupLyricTable(self, lyricsFilename):
        lyricsPath = os.path.join(self.path, "lyrics", lyricsFilename)
        self.lyricsHandler = lyricsHandler(lyricsPath)

        self.lyricMode = 0

        self.lyricsHandler.lyricTableHandler.source.selected.on_change(
            'indices', self.dataTableCallback)
        #create a loop toggle button and assigns a callback to it
        self.lyricsHandler.lyricModeButton.on_click(self.lyricModeCallback)

        self.lyricsGui = self.lyricsHandler.gui
Ejemplo n.º 16
0
click_reset_tool = CustomJS(args=dict(p=plot),
                            code="""
        p.toolbar.tools[""" + util.reset_tool_index(g.TOOLS3) +
                            """].trigger('do');
    """)

device_slc.on_change('value', device_slc_callback)
device_slc.js_on_change(
    'value',
    CustomJS(args=dict(btn=clear_everything_btn),
             code="""
        document.getElementById('modelid_' + btn.id).click();
    """))

device_conn_btn.on_click(device_conn_btn_callback)

conn_status.js_on_change(
    'value',
    CustomJS(args=dict(btn=device_conn_btn),
             code="""
        console.log('This application is connected to ESP device: ' + cb_obj.value);

        if(cb_obj.value) {
            btn.label = "Disconnect from device";
            btn.button_type = "warning";
        } else {
            btn.label = "Connect to device";
            btn.button_type = "success";
        }
    """))
Ejemplo n.º 17
0
        label="Start streaming",
        button_type="primary",
    )


source1.on_change('selected', clear_message)

device_slc.on_change('value', device_slc_callback)
device_slc.js_on_change(
    'value',
    CustomJS(args=dict(btn=reset_stream_btn),
             code="""
        document.getElementById('modelid_' + btn.id).click();
    """))

device_conn_btn.on_click(device_conn_btn_callback)
device_conn_btn.callback = CustomJS(args=dict(btn=stream_data_btn),
                                    code="""
        if (!btn.active) {
            document.getElementById('modelid_' + btn.id).click();
        }
    """)

conn_status.js_on_change(
    'value',
    CustomJS(args=dict(btn=device_conn_btn),
             code="""
        console.log('This application is connected to ESP device: ' + cb_obj.value);

        if(cb_obj.value) {
            btn.label = "Disconnect from device";
Ejemplo n.º 18
0
class CustomCounterWidget(BaseWidget):
    """Produces a widget for plotting any counters"""

    def __init__(self, doc, refresh_rate=1000, collection=None, **kwargs):
        """Produces a widget that allows the user to add / remove plots for any
        counters from any collection

        Arguments
        ---------
        doc : Bokeh Document
            bokeh document for auto-updating the widget
        refresh_rate : int
            refresh rate at which the Select refreshes and checks for new data collections (in ms)
        **kwargs
            arguments for the bokeh Select widget
        """
        super().__init__(doc, refresh_rate=refresh_rate, collection=collection, **kwargs)

        self._defaults_opts = dict(plot_width=800, plot_height=300)
        self._defaults_opts.update((key, value) for key, value in kwargs.items())

        self._lines = {}
        self._lines_info = set()
        self._line_counter = 0

        # Buttons for editing the lines
        self._add_line_b = Button(label="+", width=40)
        self._add_line_b.on_click(self._add_line)

        # Toggle button for the shading of the plots
        self._shade_b = Toggle(label="Toggle plot shading", width=150)
        self._shade_b.on_click(self._toggle_shade)

        # Buttons for adding and removing plots
        self._add_plot_b = Button(label="+", width=40)
        self._add_plot_b.on_click(self._add_plot)

        self._remove_plot_b = Button(label="-", width=40)
        self._remove_plot_b.on_click(self._remove_plot)

        # For editing the lines
        self._edit_button = Toggle(label="Edit lines", width=100)
        self._edit_button.on_click(self._toggle_edit)

        self._json_input = TextAreaInput(
            title="Export / inport widget:", width=500, max_length=20000
        )
        self._json_update_button = Button(label="Update from input", width=150)
        self._json_update_button.on_click(self._set_from_input)

        self._save_button = Button(label="Save state of widget to session", width=170)
        self._save_button.on_click(self._save_widget)

        self._root = column(
            row(
                Div(text="Add or remove plots:"),
                self._remove_plot_b,
                self._add_plot_b,
                self._edit_button,
                self._shade_b,
                self._save_button,
            ),
            empty_placeholder(),
            empty_placeholder(),
        )

        self._plots = []
        self._add_plot()

        # If there is a saved state in the session of the widget
        json_txt = DataAggregator().get_custom_widget_config()
        if json_txt:
            self.from_json(json_txt)

    def _remove_line(self, idx):
        del self._lines[idx]
        self._update_line_widget()

    def _add_line(self, update=True):
        plots_text = [f"Plot {i + 1}" for i, _ in enumerate(self._plots)]
        self._line_counter += 1
        self._lines[self._line_counter] = SelectCustomLine(
            self._doc,
            self._line_counter,
            plots_text,
            self._remove_line,
        )
        if update:
            self._update_line_widget()

    def _toggle_shade(self, shade):
        for plot in self._plots:
            plot.toggle_shade()

    def _save_widget(self):
        DataAggregator().set_custom_widget_config(json.loads(self.to_json()))

    def _update_plots(self):
        plots = [plot.layout() for plot in self._plots]
        self._root.children[2] = column(*plots)

        # Update the lines with the available plots
        plots_text = [f"Plot {i + 1}" for i, _ in enumerate(self._plots)]
        for line in self._lines.values():
            line.set_plots(plots_text)

    def _update_line_widget(self):
        lines = [line.layout() for line in self._lines.values()]
        self._root.children[1] = column(
            row(self._json_input, self._json_update_button),
            row(self._add_line_b, Div(text="Add line")),
            *lines,
        )

    def _toggle_edit(self, edit):
        if edit:
            self._update_line_widget()
        else:
            self._root.children[1] = empty_placeholder()

    def _add_plot(self):
        opts = copy.deepcopy(self._defaults_opts)
        self._plots.append(
            generator.TimeSeries(
                self._doc,
                refresh_rate=self._refresh_rate,
                title=f"Plot {len(self._plots) + 1}",
                **opts,
            )
        )
        self._update_plots()

    def _set_from_input(self):
        self._toggle_edit(False)
        self._edit_button.active = False
        self.from_json(self._json_input.value)

    def to_json(self):
        """Converts the state of the widget (number of plots, lines) to json"""
        json_dict = {"num_plots": len(self._plots), "lines": []}
        for plot_id, _, countername, instance, name in self._lines_info:
            json_dict["lines"].append(
                {"plot_id": plot_id, "countername": countername, "instance": instance, "name": name}
            )
        return json.dumps(json_dict)

    def from_json(self, json_txt):
        """Takes a json as input and generates the corresponding plots and widgets.
        Returns True if successful, False otherwise."""
        json_dict = {}
        try:
            json_dict = json.loads(json_txt.rstrip())
        except json.decoder.JSONDecodeError as e:
            logger.error(f"JSON decode error: {e.msg}")

        if "lines" not in json_dict:
            return False

        num_plots = 1
        if "num_plots" in json_dict:
            num_plots = json_dict["num_plots"]

        # Remove all the lines
        self._lines.clear()

        # Set the correct number of plots
        if num_plots > len(self._plots):
            for _ in range(num_plots - len(self._plots)):
                self._add_plot()
        elif num_plots < len(self._plots):
            for _ in range(len(self._plots) - num_plots):
                self._remove_plot()

        for line in json_dict["lines"]:
            if not isinstance(line, dict):
                return False
            if (
                "plot_id" not in line
                or "countername" not in line
                or "instance" not in line
                or "name" not in line
            ):
                return False

            if not from_instance(tuple(line["instance"])):
                return False

            locality_id, pool, thread_id = from_instance(line["instance"])

            self._add_line(False)
            self._lines[self._line_counter].set_properties(
                line["plot_id"],
                None,
                line["countername"],
                locality_id,
                pool,
                thread_id,
                line["name"],
            )

        return True

    def update(self):
        lines = set()
        for line in self._lines.values():
            lines.add(line.properties())

        deleted_lines = self._lines_info.difference(lines)
        new_lines = lines.difference(self._lines_info)

        for plot_id, collection, countername, instance, name in deleted_lines:
            if len(self._plots) >= plot_id:
                self._plots[plot_id - 1].remove_line(countername, instance, collection, name)

        for plot_id, collection, countername, instance, name in new_lines:
            self._plots[plot_id - 1].add_line(countername, instance, collection, name)

        self._lines_info = lines

        self._json_input.value = self.to_json()

    def _remove_plot(self):
        if len(self._plots) == 1:
            return
        del self._plots[-1]
        self._update_plots()
Ejemplo n.º 19
0
                    item.value = 'best_mt_score'
            except AttributeError:
                continue

    if presets.labels[presets.active] == "Tumor Clonality and Expression":
        for item in widgets:
            try:
                if item.title == 'X-Axis Value':
                    item.value = 'tumor_dna_vaf'
                elif item.title == 'Y-Axis Value':
                    item.value = 'tumor_rna_vaf'
            except AttributeError:
                continue

#Add callbacks to the 3 widgets manually created back at the start
x_field.on_change('value', lambda a,r,g: update())
y_field.on_change('value', lambda a,r,g: update())
presets.on_change('active', lambda a,r,g: change_preset())
hide_null.on_click(lambda arg: update())

#Add all models and widgets to the document
box = widgetbox(*widgets, sizing_mode='stretch_both')
fig = column(
    row(box, p),
    table,
    sizing_mode='scale_width'
)
update() #initial update
curdoc().add_root(fig)
curdoc().title = sample
Ejemplo n.º 20
0
    TableColumn(field="value_all", title="All Data"),
    TableColumn(field="value_selection", title="Selected Data"),
]

data_table = DataTable(source=datatable_source,
                       columns=datatable_columns,
                       width=450,
                       height=125,
                       index_position=None)

# callback for updating the plot based on a changes to inputs
station_name_input.on_change('value', update_station)
simulation_number_input.on_change('value', update_n_simulations)
msmt_error_input.on_change('value', update_msmt_error)
sample_size_input.on_change('value', update_simulation_sample_size)
toggle_button.on_click(update_simulated_msmt_error)

# see documentation for threading information
# https://docs.bokeh.org/en/latest/docs/user_guide/server.html

update()

# widgets
ts_plot = create_ts_plot(peak_source, peak_flagged_source)

peak_source.selected.on_change('indices', update_UI)

vh1, pv, hist_source = create_vhist(peak_source, ts_plot)

ffa_plot = create_ffa_plot(peak_source, peak_flagged_source,
                           distribution_source)
Ejemplo n.º 21
0

def save(self):
    filename = 'sensitivity_curve_' + str(armlength.value) + '_' + str(
        wavelength.value) + '_' + str(power.value) + '_' + str(
            trans.value) + '_' + str(sqzamp.value) + '_' + str(
                sqzang.value) + '_' + str(sqzlength.value) + '_' + str(
                    sqzdet.value)
    with open(filename, 'a') as f:
        for i, j in zip(np.logspace(1, 4, 10000), np.sqrt(traces["Total"][0])):
            #print(i, j)
            f.write('%f %e\n' % (i, j))


toggle = Toggle(label="Total noise", button_type="warning")
toggle.on_click(totalnoise_handler)

toggle_aL = Toggle(label="aLIGO", button_type="warning")
toggle_aL.on_click(totalnoise_handler_aL)

toggle_save = Toggle(label="Save sensitivity curve", button_type="success")
toggle_save.on_click(save)

#if (toggle=active)


# Set up callbacks
def update_data(attrname, old, new):

    # Get the current slider values
    L = armlength.value
Ejemplo n.º 22
0
                max(i if i else 0 for i in data[x] or [1]))
    y_minmax = (min(i if i else 0 for i in data[y] or [0]),
                max(i if i else 0 for i in data[y] or [1]))
    p.x_range.start = x_minmax[0] - (x_minmax[1] - x_minmax[0] + 1) / 20
    p.x_range.end = x_minmax[1] + (x_minmax[1] - x_minmax[0] + 1) / 20
    p.y_range.start = y_minmax[0] - (y_minmax[1] - y_minmax[0] + 1) / 20
    p.y_range.end = y_minmax[1] + (y_minmax[1] - y_minmax[0] + 1) / 20


# Maintain current axes ranges upon reset
p.on_event('reset', readjust_axes)

# Add callbacks to the 3 widgets manually created back at the start
x_field.on_change('value', lambda a, r, g: update())
y_field.on_change('value', lambda a, r, g: update())
hide_null.on_click(lambda arg: update())

download_button = Button(label="Download", button_type="success")
download_button.callback = CustomJS(args=dict(source=source),
                                    code=open(
                                        join(dirname(__file__),
                                             "js/csv_download_all.js")).read())

download_selected_button = Button(label="Download Selected",
                                  button_type="success")
download_selected_button.callback = CustomJS(
    args=dict(source=source),
    code=open(join(dirname(__file__), "js/csv_download_selected.js")).read())

widgets.extend((download_button, download_selected_button))
Ejemplo n.º 23
0
},
                    code=code)
hover = HoverTool(tooltips=[('Sites', '@sites'), ('Type', '@type'),
                            ('Year', '@year')],
                  callback=callback,
                  renderers=[el])
p.add_tools(hover)

hover2 = HoverTool(tooltips=[('Name', '@name'), ('Type', '@type'),
                             ('Year', '@year')],
                   renderers=[cr])
p.add_tools(hover2)

#add toggle buttons for ellipses and markers
toggleEllipses = Toggle(label="Hide Ellipses", active=True)
toggleEllipses.on_click(updateEllipses)

toggleMarkers = Toggle(label="Hide Markers", active=True)
toggleMarkers.on_click(updateMarkers)

#add slider
slider = Slider(title='Time',
                start=minTime,
                end=maxTime,
                step=1,
                value=minTime)
slider.on_change('value', update_plot)

#add play button
bPlay = Toggle(label="Play", active=False, button_type="success")
bPlay.on_click(automateMap)
Ejemplo n.º 24
0
x.on_change('value', update_plot)

# create y-axis dropdown widget with continuous var columns
y = Select(title='Y-Axis', value=Y_INIT, options=continuous)
# link widget callback to update_plot()
y.on_change('value', update_plot)

# create dot color dropdown widget with "countable" var columns
color = Select(title='Color', value=COLOR_INIT, options=countable)
# link widget callback to update_plot()
color.on_change('value', update_plot)

# create image glyph toggle button
toggle = Toggle(label="Show Images", button_type="success")
# link button callback to toggle_callback()
toggle.on_click(toggle_callback)

# create lasso
lasso = LassoSelectTool()

# download button
button = Button(label="Download", button_type="success")

# button.callback = download_callback()
button.callback = CustomJS(args=dict(source=source),
                           code=open(join(dirname(__file__),
                                          "download.js")).read())

# add button and dropdown selections to a widgetbox
widgets = [x, y, color, toggle, button]
controls = widgetbox(widgets, sizing_mode='scale_both')
Ejemplo n.º 25
0
from bokeh.models.layouts import WidgetBox
from bokeh.models.widgets import (
    Button, Toggle, Dropdown, CheckboxGroup, RadioGroup,
    CheckboxButtonGroup, RadioButtonGroup,
)

button = Button(label="Button (enabled) - has click event", button_type="primary")
button.on_click(lambda: print('button: click'))
button.js_on_click(CustomJS(code="console.log('button: click', this.toString())"))

button_disabled = Button(label="Button (disabled) - no click event", button_type="primary", disabled=True)
button_disabled.on_click(lambda: print('button_disabled: click'))
button_disabled.js_on_click(CustomJS(code="console.log('button_disabled: click', this.toString())"))

toggle_inactive = Toggle(label="Toggle button (initially inactive)", button_type="success")
toggle_inactive.on_click(lambda value: print('toggle_inactive: %s' % value))
toggle_inactive.js_on_click(CustomJS(code="console.log('toggle_inactive: ' + this.active, this.toString())"))

toggle_active = Toggle(label="Toggle button (initially active)", button_type="success", active=True)
toggle_active.on_click(lambda value: print('toggle_active: %s' % value))
toggle_active.js_on_click(CustomJS(code="console.log('toggle_active: ' + this.active, this.toString())"))

menu = [("Item 1", "item_1_value"), ("Item 2", "item_2_value"), None, ("Item 3", "item_3_value")]

dropdown = Dropdown(label="Dropdown button", button_type="warning", menu=menu)
dropdown.on_click(lambda value: print('dropdown: %s' % value))
dropdown.js_on_click(CustomJS(code="console.log('dropdown: ' + this.value, this.toString())"))

dropdown_disabled = Dropdown(label="Dropdown button (disabled)", button_type="warning", menu=menu)
dropdown_disabled.on_click(lambda value: print('dropdown_disabled: %s' % value))
dropdown_disabled.js_on_click(CustomJS(code="console.log('dropdown_disabled: ' + this.value, this.toString())"))
Ejemplo n.º 26
0
button = Button(label="Button (enabled) - has click event",
                button_type="primary")
button.on_click(lambda: print('button: click'))
button.js_on_click(
    CustomJS(code="console.log('button: click', this.toString())"))

button_disabled = Button(label="Button (disabled) - no click event",
                         button_type="primary",
                         disabled=True)
button_disabled.on_click(lambda: print('button_disabled: click'))
button_disabled.js_on_click(
    CustomJS(code="console.log('button_disabled: click', this.toString())"))

toggle_inactive = Toggle(label="Toggle button (initially inactive)",
                         button_type="success")
toggle_inactive.on_click(lambda value: print('toggle_inactive: %s' % value))
toggle_inactive.js_on_click(
    CustomJS(
        code="console.log('toggle_inactive: ' + this.active, this.toString())")
)

toggle_active = Toggle(label="Toggle button (initially active)",
                       button_type="success",
                       active=True)
toggle_active.on_click(lambda value: print('toggle_active: %s' % value))
toggle_active.js_on_click(
    CustomJS(
        code="console.log('toggle_active: ' + this.active, this.toString())"))

menu = [("Item 1", "item_1_value"), ("Item 2", "item_2_value"), None,
        ("Item 3", "item_3_value")]
Ejemplo n.º 27
0
### set up callbacks

##all_image

data_directory_text_input.on_change("value", data_directory_text_handler)

refresh_directory_button.on_click(refresh_directory)

#select_channel.on_change('value', select_channel_handler)

color_palette_menu.on_change('value', select_palette_handler)

fwd_bwd_button.on_click(fwd_bwd_handler)

select_all_button.on_click(button_change_all)

grid_view_button.on_click(grid_view_handler)

update_button.on_click(update)

update_resolution.on_click(update_plot_ranges)

max_slider.on_change("value", slider_callback_high)
min_slider.on_change("value", slider_callback_low)

## single image viewer

select_file.on_change('value', select_file_handler)

file_forward_button.on_click(lambda: button_change_index(1))