Beispiel #1
0
    def test_displays_button_type(self, typ, bokeh_model_page):
        button = Toggle(button_type=typ, css_classes=["foo"])

        page = bokeh_model_page(button)

        button = page.driver.find_element_by_css_selector('.foo .bk-btn')
        assert typ in button.get_attribute('class')
Beispiel #2
0
    def test_js_on_click_executes(self, bokeh_model_page):
        button = Toggle(css_classes=['foo'])
        button.js_on_click(CustomJS(code=RECORD("value", "cb_obj.active")))

        page = bokeh_model_page(button)

        button = page.driver.find_element_by_css_selector('.foo .bk-btn')
        button.click()

        results = page.results
        assert results == {'value': True}

        button = page.driver.find_element_by_css_selector('.foo .bk-btn')
        button.click()

        results = page.results
        assert results == {'value': False}

        button = page.driver.find_element_by_css_selector('.foo .bk-btn')
        button.click()

        results = page.results
        assert results == {'value': True}

        assert page.has_no_console_errors()
Beispiel #3
0
 def modify_doc(doc):
     source = ColumnDataSource(dict(x=[1, 2], y=[1, 1]))
     plot = Plot(plot_height=400, plot_width=400, x_range=Range1d(0, 1), y_range=Range1d(0, 1), min_border=0)
     plot.add_glyph(source, Circle(x='x', y='y', size=20))
     plot.add_tools(CustomAction(callback=CustomJS(args=dict(s=source), code=RECORD("data", "s.data"))))
     button = Toggle(css_classes=['foo'])
     def cb(value):
         if value:
             source.data=dict(x=[10, 20], y=[10, 10])
         else:
             source.data=dict(x=[100, 200], y=[100, 100])
     button.on_click(cb)
     doc.add_root(column(button, plot))
Beispiel #4
0
def plotPairs(fileName, bokehPlaceholderId='bokehContent'):    

    source = ColumnDataSource(data={'count':[], 'index':[]})
    minCount = TextInput(value="20", title="Minimum number of trips per pair:")
    checkbox_group = CheckboxGroup(labels=["Include trips from a station to itself."])
    ShowButton = Toggle(label="Show", type="success")
    model = dict(source=source, checkbox = checkbox_group, minCount = minCount)
    plot = Figure(title="Accumulative Number Of Trips Over Top Station Pairs", x_axis_label='Top Station Pairs in Decreasing Order', y_axis_label='Accumulative Number of Trips', plot_width=1200, plot_height=400)
    plot.ray(x = [0], y=[90], line_color='red', angle=0.0, name='90%', length = 0, line_width=2)
    plot.ray(x = [0], y=[90], line_color='red', angle=180.0, angle_units = 'deg', length = 0, line_width=2)
    plot.line(x = 'index', y ='count', source=source, line_width=2, line_alpha=0.6)
    
    callback = CustomJS(args=model, code="""
            var selectSelf = checkbox.get('active').length;
            var minCountVal = minCount.get('value');
            var xmlhttp;
            xmlhttp = new XMLHttpRequest();
            
            xmlhttp.onreadystatechange = function() {
                if (xmlhttp.readyState == XMLHttpRequest.DONE ) {
                    if(xmlhttp.status == 200){
                        var data = source.get('data');
                        var result = JSON.parse(xmlhttp.responseText);
                        
                        data['count'] = result.count;
                        data['index'] = result.index;
                        source.trigger('change');
                        alert('triggered');
                    }
                    else if(xmlhttp.status == 400) {
                        alert(400);
                    }
                    else {
                        alert(xmlhttp.status);
                    }
                }
            };
        var params = {self:selectSelf, min:minCountVal};
        url = "/pairsdist?" + jQuery.param( params );
        xmlhttp.open("GET", url, true);
        xmlhttp.send();
        """)
        
    ShowButton.callback = callback
    layout = vform(checkbox_group, minCount, ShowButton, plot)
    script, div = components(layout)
    html = readHtmlFile(fileName)
    html = insertScriptIntoHeader(html, script)
    html = appendElementContent(html, div, "div", "bokehContent")

    return html
Beispiel #5
0
file_selection_button.on_click(load_file)

files_selector_spacer = Spacer(width=10)

group_selection_button = Button(label="Select Directory",
                                button_type="primary",
                                width=140)
group_selection_button.on_click(load_directory_group)

update_files_button = Button(label="Update Files",
                             button_type="default",
                             width=50)
update_files_button.on_click(reload_all_files)

auto_update_toggle_button = Toggle(label="Auto Update",
                                   button_type="default",
                                   width=50,
                                   active=True)
auto_update_toggle_button.on_click(toggle_auto_update)

unload_file_button = Button(label="Unload", button_type="danger", width=50)
unload_file_button.on_click(unload_file)

# files selection box
files_selector = Select(title="Files:", options=[""])
files_selector.on_change('value', change_data_selector)

# data selection box
data_selector = MultiSelect(title="Data:", options=[], size=12)
data_selector.on_change('value', select_data)

# x axis selection box
Beispiel #6
0
plot_r2.add_layout(trial_type_b_corr)

# Set up widgets
data_title = Div(text="<b>Correlation Settings</b>",
                 style={'font-size': '100%'}, width=200, height=30)
corr_a_widget = Slider(title="Trial Type A Correlation", value=corr_a, start=-1, end=1, step=0.1)
corr_b_widget = Slider(title="Trial Type B Correlation", value=corr_b, start=-1, end=1, step=0.1)
noise_widget = Slider(title="noise", value=0, start=0, end=0.1, step=0.01)


widgets = [data_title,
           corr_a_widget,
           corr_b_widget,
           noise_widget]

beta_a_toggle = Toggle(label="Show A Betas", button_type="success", active=True)
beta_b_toggle = Toggle(label="Show B Betas", button_type="success", active=True)


for y0, y1, plot, beta_key, in zip([-0.2, 0.04],
                                   [0, 0.08],
                                   [plot_r1, plot_r2],
                                   ['betas_r1', 'betas_r2']):
    for idx, (onsets, betas, toggle, source) in enumerate(zip([onsets_a, onsets_b],
                                                              [betas_a, betas_b],
                                                              [beta_a_toggle, beta_b_toggle],
                                                              [source_la, source_lb])):
        y0s = y0 * len(onsets)
        y1s = y1 * len(onsets)
        lbls = LabelSet(x='onsets', y=0.35,
                        text=beta_key, source=source,
Beispiel #7
0
from bokeh.layouts import layout
from bokeh.models import BoxAnnotation, Toggle
from bokeh.plotting import figure

output_file("styling_visible_annotation_with_interaction.html")

p = figure(plot_width=600, plot_height=200, tools='')
p.line([1, 2, 3], [1, 2, 1], line_color="blue")
pink_line = p.line([1, 2, 3], [2, 1, 2], line_color="pink")

green_box = BoxAnnotation(left=1.5, right=2.5, fill_color='green', fill_alpha=0.1)
p.add_layout(green_box)

# Use js_link to connect button active property to glyph visible property

toggle1 = Toggle(label="Green Box", button_type="success", active=True)
toggle1.js_link('active', green_box, 'visible')

toggle2 = Toggle(label="Pink Line", button_type="success", active=True)
toggle2.js_link('active', pink_line, 'visible')

show(layout([p], [toggle1, toggle2]))

#######

# dimensions

from bokeh.plotting import figure, output_file, show

output_file("dimensions.html")
Beispiel #8
0
def aggregate_plot(tb):
    """
    Function for creating a bokeh plot that shows aggregate tax liabilities for
    each year the TaxBrain instance was run
    Parameters
    ----------
    tb: An instance of the TaxBrain object
    Returns
    -------
    Bokeh figure
    """
    # Pull aggregate data by year and transpose it for plotting
    varlist = ["iitax", "payrolltax", "combined"]
    base_data = tb.multi_var_table(varlist, "base").transpose()
    base_data["calc"] = "Base"
    reform_data = tb.multi_var_table(varlist, "reform").transpose()
    reform_data["calc"] = "Reform"
    base_cds = ColumnDataSource(base_data)
    reform_cds = ColumnDataSource(reform_data)
    num_ticks = len(base_data)
    del base_data, reform_data

    fig = figure(title="Aggregate Tax Liability by Year",
                 width=700,
                 height=500,
                 tools="save")
    ii_base = fig.line(x="index",
                       y="iitax",
                       line_width=4,
                       line_color="#12719e",
                       legend="Income Tax - Base",
                       source=base_cds)
    ii_reform = fig.line(x="index",
                         y="iitax",
                         line_width=4,
                         line_color="#73bfe2",
                         legend="Income Tax - Reform",
                         source=reform_cds)
    proll_base = fig.line(x="index",
                          y="payrolltax",
                          line_width=4,
                          line_color="#408941",
                          legend="Payroll Tax - Base",
                          source=base_cds)
    proll_reform = fig.line(x="index",
                            y="payrolltax",
                            line_width=4,
                            line_color="#98cf90",
                            legend="Payroll Tax - Reform",
                            source=reform_cds)
    comb_base = fig.line(x="index",
                         y="combined",
                         line_width=4,
                         line_color="#a4201d",
                         legend="Combined - Base",
                         source=base_cds)
    comb_reform = fig.line(x="index",
                           y="combined",
                           line_width=4,
                           line_color="#e9807d",
                           legend="Combined - Reform",
                           source=reform_cds)

    # format figure
    fig.legend.location = "top_left"
    fig.yaxis.formatter = NumeralTickFormatter(format="$0.00a")
    fig.yaxis.axis_label = "Aggregate Tax Liability"
    fig.xaxis.minor_tick_line_color = None
    fig.xaxis[0].ticker.desired_num_ticks = num_ticks

    # Add hover tool
    tool_str = """
        <p><b>@calc - {}</b></p>
        <p>${}</p>
    """
    ii_hover = HoverTool(tooltips=tool_str.format("Individual Income Tax",
                                                  "@iitax{0,0}"),
                         renderers=[ii_base, ii_reform])
    proll_hover = HoverTool(tooltips=tool_str.format("Payroll Tax",
                                                     "@payrolltax{0,0}"),
                            renderers=[proll_base, proll_reform])
    combined_hover = HoverTool(tooltips=tool_str.format(
        "Combined Tax", "@combined{0,0}"),
                               renderers=[comb_base, comb_reform])
    fig.add_tools(ii_hover, proll_hover, combined_hover)

    # toggle which lines are shown
    plot_js = """
    object1.visible = toggle.active
    object2.visible = toggle.active
    object3.visible = toggle.active
    """
    base_callback = CustomJS.from_coffeescript(code=plot_js, args={})
    base_toggle = Toggle(label="Base",
                         button_type="primary",
                         callback=base_callback,
                         active=True)
    base_callback.args = {
        "toggle": base_toggle,
        "object1": ii_base,
        "object2": proll_base,
        "object3": comb_base
    }

    reform_callback = CustomJS.from_coffeescript(code=plot_js, args={})
    reform_toggle = Toggle(label="Reform",
                           button_type="primary",
                           callback=reform_callback,
                           active=True)
    reform_callback.args = {
        "toggle": reform_toggle,
        "object1": ii_reform,
        "object2": proll_reform,
        "object3": comb_reform
    }
    fig_layout = layout([fig], [base_toggle, reform_toggle])

    # Components needed to embed the figure
    data = json_item(fig_layout)
    outputs = {
        "media_type": "bokeh",
        "title": "",
        "data": data,
    }

    return outputs
Beispiel #9
0
def plot_benchmarks_as_lines(title,
                             *bm,
                             transform=None,
                             line_title_transform=None,
                             logx=True,
                             logy=True):
    import bokeh
    from bokeh.plotting import figure, output_file, show
    from bokeh.palettes import Dark2_5 as palette
    from bokeh.layouts import row, column
    from bokeh.models import (Legend, LegendItem, CheckboxGroup, CustomJS, Div,
                              RadioGroup, Toggle, ColumnDataSource, DataTable,
                              TableColumn)
    from bokeh.models.markers import marker_types
    #
    ids = entry_ids(*bm, transform=transform)
    colors = itertools.cycle(palette)
    markers = itertools.cycle(marker_types)
    p = figure(
        title=title,
        x_axis_type="log" if logx else "linear",
        y_axis_type="log" if logy else "linear",
        #background_fill_color="#fafafa",
        plot_width=1000,
        x_axis_label="Number of pixels",
        y_axis_label="Throughput (MB/s)",
    )
    p.toolbar.autohide = True
    #p.toolbar.active_inspect = [hover_tool, crosshair_tool]
    p.toolbar.active_drag = "auto"
    p.toolbar.active_scroll = "auto"

    #
    def dft(v):
        return v if v else (lambda n: n)

    tr = dft(transform)
    lttr = dft(line_title_transform)
    #
    for results in bm:
        x = [ids[name] for name in results.names]
        y = [bps / 1e6 for bps in results.bytes_per_second]
        c = next(colors)
        marker = next(markers)
        next(markers)  # advance two
        line_name = lttr(results.first)
        #legends.append(LegendItem(name=c, label=line_name))
        p.scatter(x, y, marker=marker, size=8, color=c, legend_label=line_name)
        p.line(
            x,
            y,
            color=c,
            alpha=0.9,
            #muted_color=c, muted_alpha=0.05,
            legend_label=line_name)
    p.legend.click_policy = "hide"
    p.legend.label_text_font_size = "10px"

    #
    def input_title(title):
        return Div(text=f"<h3>{title}</h3>")

    inputs = []
    first = bm[0].first.meta
    for k, g in first.checkbox_groups().items():
        cb = CheckboxGroup(labels=[str(v) for v in g],
                           active=[i for i in range(len(g))],
                           inline=True)
        inputs.append(input_title(k))
        inputs.append(cb)
    #
    # https://github.com/bokeh/bokeh/blob/branch-2.3/examples/app/export_csv/main.py
    x_axis_values = [f"{m.num_pixels}px" for m in bm[0].meta]
    table_sources = []
    for i, px in enumerate(x_axis_values):
        c = ColumnDataSource(
            data={
                'name': [nth(results.filtered_names, i) for results in bm],
                'bytes_per_second':
                [nth(results.bytes_per_second, i) for results in bm],
                'items_per_second':
                [nth(results.items_per_second, i) for results in bm],
                'cpu_time': [nth(results.real_time, i) for results in bm],
                'real_time': [nth(results.real_time, i) for results in bm],
                'iterations': [nth(results.iterations, i) for results in bm],
                'threads': [nth(results.threads, i) for results in bm],
            })
        table_sources.append(c)
    selected_x_index = 8  # FIXME (currently 2000 pixels)
    table_source = copy.deepcopy(table_sources[selected_x_index])
    relvalues = Toggle(label="Table: Relative values")
    px_title = input_title("Table: number of pixels")
    px_radiogroup = RadioGroup(labels=x_axis_values, active=selected_x_index)
    table_inputs = [relvalues, px_title, px_radiogroup]
    #
    table_cols = [
        TableColumn(field='name', title='Name'),
        TableColumn(field='bytes_per_second', title='Bytes/second'),
        TableColumn(field='items_per_second', title='Items/second'),
        TableColumn(field='cpu_time', title='CPU time'),
        TableColumn(field='real_time', title='Real time'),
        TableColumn(field='iterations', title='Iterations'),
        TableColumn(field='threads', title='Threads'),
    ]
    data_table = DataTable(source=table_source, columns=table_cols, width=1200)
    callback = CustomJS(args=dict(radiogroup=px_radiogroup,
                                  source=table_source,
                                  table=table_sources),
                        code="""
    console.log(`active=${radiogroup.active}`);
    /*source.data=table[radiogroup.active];*/
    var nrows = source.data['name'].length;
    var ts = table[radiogroup.active].data;
    var names = ["name", "bytes_per_second", "items_per_second", "cpu_time", "real_time", "iterations", "threads"];
    var ncols = names.length;
    console.log(`names=${names} nrows=${nrows} ncols=${ncols}`);
    for(var i = 0; i < nrows; i++) {
        for(var j = 0; j < ncols; ++j) {
           var name = names[j];
           /*console.log(`i=${i} j=${j} name=${name}`);*/
           source.data[name][i] = ts[name][i];
        }
    }
    source.change.emit();
    """)
    px_radiogroup.js_on_change('active', callback)
    #                        lambda attr, old, new: log(f"attr={attr} old={old} new={new} active={px_radiogroup.active}"))
    #
    layout = column(row(column(*inputs), p),
                    row(column(*table_inputs), data_table))
    show(layout)
Beispiel #10
0
p.add_tools(
    HoverTool(renderers=[points_render],
              tooltips=[('Var1', '@var1'), ('Var2', '@var2'),
                        ('Var3', '@var3')]))

filter_list = {}

for var in ['var1', 'var2', 'var3']:
    min_ = 0
    max_ = 100
    slider = RangeSlider(start=min_,
                         end=max_,
                         step=0.1,
                         value=(min_, max_),
                         title=f'{var} range')
    toggle = Toggle(label="Inactive", button_type="danger", aspect_ratio=3)
    toggle.js_on_click(toggle_callback(toggle))
    filter_list[var] = Filter(var, slider, toggle)


def update_plot(attrname, old, new):
    mask = [True] * len(gdf)
    for key, filter in filter_list.items():
        if filter.toggle_.active:
            mask = mask & (gdf[key] >= filter.slider_.value[0]) & (
                gdf[key] <= filter.slider_.value[1])
    test_view.filters[0] = BooleanFilter(booleans=mask)


for _, filter in filter_list.items():
    filter.slider_.on_change('value', update_plot)
Beispiel #11
0
        'mpoly': mpoly,
        'ext_datafiles': ext_datafiles,
        'line': line,
    },
             code="""
    console.log('select: value=' + this.value, this.toString())
    line.glyph.y.field = this.value
    line.data_source.change.emit()

"""))
"""
# %% Make a button
________________________________________________________________________________
"""
buttons = {}
buttons['test'] = Toggle(label="Test", visible=True, button_type='primary')  #
buttons['test'].js_on_change(
    'active',
    CustomJS(args={'p': p},
             code="""
                  if (cb_obj.active == false){
                      console.log(p.x_range)
                      console.log(p.x_range.follow)
                      console.log(p.x_range.start)
                      console.log(p.x_range.end)
                      p.x_range.setv({'start':-1.4e7,'end':-7.4e6})
                      p.change.emit()
                  }
                  else{
                      console.log(p.x_range)
                      console.log(p.x_range.follow)
Beispiel #12
0
        extension = '.txt'
    elif str(new) == '2':
        extension = '.npy'

LABELS = [".csv", ".txt", ".npy"]

extensiones_rg = RadioGroup(labels=LABELS, active=0)
extensiones_rg.on_change('active', callback_extension)


#BOTON INICIAR
def activador(active):
    global manual
    manual = not manual

act = Toggle(label="Manual/Automático", button_type="success")
act.on_click(activador)

#BOTON GUARDAR
def guardador(active):
    global guardar
    guardar = not guardar

boton_guardar = Toggle(label="Guardar", button_type="success")
boton_guardar.on_click(guardador)

# TEXT INPUT
def Kp_callback(attr, old, new):
    global Kp
    Kp = new
def __make_daybyday_interactive_timeline(
    df: pd.DataFrame,
    *,
    geo_df: geopandas.GeoDataFrame,
    value_col: str,
    transform_df_func: Callable[[pd.DataFrame], pd.DataFrame] = None,
    stage: Union[DiseaseStage, Literal[Select.ALL]] = Select.ALL,
    count: Union[Counting, Literal[Select.ALL]] = Select.ALL,
    out_file_basename: str,
    subplot_title_prefix: str,
    plot_aspect_ratio: float = None,
    cmap=None,
    n_cbar_buckets: int = None,
    n_buckets_btwn_major_ticks: int = None,
    n_minor_ticks_btwn_major_ticks: int = None,
    per_capita_denominator: int = None,
    x_range: Tuple[float, float],
    y_range: Tuple[float, float],
    min_visible_y_range: float,
    should_make_video: bool,
) -> InfoForAutoload:
    """Create the bokeh interactive timeline plot(s)

    This function takes the given DataFrame, which must contain COVID data for locations
    on different dates, and a GeoDataFrame, which contains the long/lat coords for those
    locations, and creates an interactive choropleth of the COVID data over time.

    :param df: The COVID data DataFrame
    :type df: pd.DataFrame
    :param geo_df: The geometry GeoDataFrame for the locations in `df`
    :type geo_df: geopandas.GeoDataFrame
    :param value_col: The column of `df` containing the values to plot in the
    choropleth; should be something like "Case_Counts" or "Case_Diff_From_Prev_Day"
    :type value_col: str
    :param stage: The DiseaseStage to plot, defaults to Select.ALL. If ALL, then all
    stages are plotted and are stacked vertically.
    :type stage: Union[DiseaseStage, Literal[Select.ALL]], optional
    :param count: The Counting to plot, defaults to Select.ALL. If ALL, then all
    count types are plotted and are stacked horizontally.
    :type count: Union[Counting, Literal[Select.ALL]], optional
    :param out_file_basename: The basename of the file to save the interactive plots to
    (there are two components, the JS script and the HTML <div>)
    :type out_file_basename: str
    :param subplot_title_prefix: What the first part of the subplot title should be;
    probably a function of `value_col` (if value_col is "Case_Counts" then this param
    might be "Cases" or "# of Cases")
    :type subplot_title_prefix: str
    :param x_range: The range of the x-axis as (min, max)
    :type x_range: Tuple[float, float]
    :param y_range: The range of the y-axis as (min, max)
    :type y_range: Tuple[float, float]
    :param min_visible_y_range: The minimum height (in axis units) of the y-axis; it
    will not be possible to zoom in farther than this on the choropleth.
    :type min_visible_y_range: float
    :param should_make_video: Optionally run through the timeline day by day, capture
    a screenshot for each day, and then stitch the screenshots into a video. The video
    shows the same info as the interactive plots, but not interactively. This easily
    takes 20x as long as just making the graphs themselves, so use with caution.
    :type should_make_video: bool
    :param transform_df_func: This function expects data in a certain format, and does
    a bunch of preprocessing (expected to be common) before plotting. This gives you a
    chance to do any customization on the postprocessed df before it's plotted. Defaults
    to None, in which case no additional transformation is performed.
    :type transform_df_func: Callable[[pd.DataFrame], pd.DataFrame], optional
    :param plot_aspect_ratio: The aspect ratio of the plot as width/height; if set, the
    aspect ratio will be fixed to this. Defaults to None, in which case the aspect ratio
    is determined from the x_range and y_range arguments
    :type plot_aspect_ratio: float, optional
    :param cmap: The colormap to use as either a matplotlib-compatible colormap or a
    list of hex strings (e.g., ["#ae8f1c", ...]). Defaults to None in which case a
    reasonable default is used.
    :type cmap: Matplotlib-compatible colormap or List[str], optional
    :param n_cbar_buckets: How many colorbar buckets to use. Has little effect if the
    colormap is continuous, but always works in conjunction with
    n_buckets_btwn_major_ticks to determine the number of major ticks. Defaults to 6.
    :type n_cbar_buckets: int, optional
    :param n_buckets_btwn_major_ticks: How many buckets are to lie between colorbar
    major ticks, determining how many major ticks are drawn. Defaults to 1.
    :type n_buckets_btwn_major_ticks: int, optional
    :param n_minor_ticks_btwn_major_ticks: How many minor ticks to draw between colorbar
    major ticks. Defaults to 8 (which means each pair of major ticks has 10 ticks
    total).
    :type n_minor_ticks_btwn_major_ticks: int, optional
    :param per_capita_denominator: When describing per-capita numbers, what to use as
    the denominator (e.g., cases per 100,000 people). If None, it is automatically
    computed per plot to be appropriately scaled for the data.
    :type per_capita_denominator: int, optional
    :raises ValueError: [description]
    :return: The two pieces of info required to make a Bokeh autoloading HTML+JS plot:
    the HTML div to be inserted somewhere in the HTML body, and the JS file that will
    load the plot into that div.
    :rtype: InfoForAutoload
    """

    Counting.verify(count, allow_select=True)
    DiseaseStage.verify(stage, allow_select=True)

    # The date as a string, so that bokeh can use it as a column name
    STRING_DATE_COL = "String_Date_"
    # A column whose sole purpose is to be a (the same) date associated with each
    # location
    FAKE_DATE_COL = "Fake_Date_"
    # The column we'll actually use for the colors; it's computed from value_col
    COLOR_COL = "Color_"

    # Under no circumstances may you change this date format
    # It's not just a pretty date representation; it actually has to match up with the
    # date strings computed in JS
    DATE_FMT = r"%Y-%m-%d"

    ID_COLS = [
        REGION_NAME_COL,
        Columns.DATE,
        Columns.STAGE,
        Columns.COUNT_TYPE,
    ]

    if cmap is None:
        cmap = cmocean.cm.matter

    if n_cbar_buckets is None:
        n_cbar_buckets = 6

    if n_buckets_btwn_major_ticks is None:
        n_buckets_btwn_major_ticks = 1

    if n_minor_ticks_btwn_major_ticks is None:
        n_minor_ticks_btwn_major_ticks = 8

    n_cbar_major_ticks = n_cbar_buckets // n_buckets_btwn_major_ticks + 1

    try:
        color_list = [
            # Convert matplotlib colormap to bokeh (list of hex strings)
            # https://stackoverflow.com/a/49934218
            RGB(*rgb).to_hex()
            for i, rgb in enumerate((255 * cmap(range(256))).astype("int"))
        ]
    except TypeError:
        color_list = cmap

    color_list: List[BokehColor]

    if stage is Select.ALL:
        stage_list = list(DiseaseStage)
    else:
        stage_list = [stage]

    if count is Select.ALL:
        count_list = list(Counting)
    else:
        count_list = [count]

    stage_list: List[DiseaseStage]
    count_list: List[Counting]

    stage_count_list: List[Tuple[DiseaseStage, Counting]] = list(
        itertools.product(stage_list, count_list))

    df = df.copy()

    # Unadjust dates (see SaveFormats._adjust_dates)
    normalized_dates = df[Columns.DATE].dt.normalize()
    is_at_midnight = df[Columns.DATE] == normalized_dates
    df.loc[is_at_midnight, Columns.DATE] -= pd.Timedelta(days=1)
    df.loc[~is_at_midnight, Columns.DATE] = normalized_dates[~is_at_midnight]

    min_date, max_date = df[Columns.DATE].agg(["min", "max"])
    dates: List[pd.Timestamp] = pd.date_range(start=min_date,
                                              end=max_date,
                                              freq="D")
    max_date_str = max_date.strftime(DATE_FMT)

    # Get day-by-day case diffs per location, date, stage, count-type

    # Make sure data exists for every date for every state so that the entire country is
    # plotted each day; fill missing data with 0 (missing really *is* as good as 0)
    # enums will be replaced by their name (kind of important)
    id_cols_product: pd.MultiIndex = pd.MultiIndex.from_product(
        [
            df[REGION_NAME_COL].unique(),
            dates,
            [s.name for s in DiseaseStage],
            [c.name for c in Counting],
        ],
        names=ID_COLS,
    )

    df = (id_cols_product.to_frame(index=False).merge(
        df,
        how="left",
        on=ID_COLS,
    ).sort_values(ID_COLS))

    df[STRING_DATE_COL] = df[Columns.DATE].dt.strftime(DATE_FMT)
    df[Columns.CASE_COUNT] = df[Columns.CASE_COUNT].fillna(0)

    if transform_df_func is not None:
        df = transform_df_func(df)

    df = geo_df.merge(df, how="inner", on=REGION_NAME_COL)[[
        REGION_NAME_COL,
        Columns.DATE,
        STRING_DATE_COL,
        Columns.STAGE,
        Columns.COUNT_TYPE,
        value_col,
    ]]

    dates: List[pd.Timestamp] = [
        pd.Timestamp(d) for d in df[Columns.DATE].unique()
    ]

    values_mins_maxs = (df[df[value_col] > 0].groupby(
        [Columns.STAGE, Columns.COUNT_TYPE])[value_col].agg(["min", "max"]))

    vmins: pd.Series = values_mins_maxs["min"]
    vmaxs: pd.Series = values_mins_maxs["max"]

    pow10s_series: pd.Series = vmaxs.map(
        lambda x: int(10**(-np.floor(np.log10(x)))))

    # _pow_10s_series_dict = {}
    # for stage in DiseaseStage:
    #     _pow_10s_series_dict.update(
    #         {
    #             (stage.name, Counting.TOTAL_CASES.name): 100000,
    #             (stage.name, Counting.PER_CAPITA.name): 10000,
    #         }
    #     )

    # pow10s_series = pd.Series(_pow_10s_series_dict)

    vmins: dict = vmins.to_dict()
    vmaxs: dict = vmaxs.to_dict()

    for stage in DiseaseStage:
        _value_key = (stage.name, Counting.PER_CAPITA.name)
        if per_capita_denominator is None:
            _max_pow10 = pow10s_series.loc[(slice(None),
                                            Counting.PER_CAPITA.name)].max()
        else:
            _max_pow10 = per_capita_denominator

        vmins[_value_key] *= _max_pow10
        vmaxs[_value_key] *= _max_pow10
        pow10s_series[_value_key] = _max_pow10

    percap_pow10s: pd.Series = df.apply(
        lambda row: pow10s_series[
            (row[Columns.STAGE], row[Columns.COUNT_TYPE])],
        axis=1,
    )

    _per_cap_rows = df[Columns.COUNT_TYPE] == Counting.PER_CAPITA.name
    df.loc[_per_cap_rows, value_col] *= percap_pow10s.loc[_per_cap_rows]

    # Ideally we wouldn't have to pivot, and we could do a JIT join of state longs/lats
    # after filtering the data. Unfortunately this is not possible, and a long data
    # format leads to duplication of the very large long/lat lists; pivoting is how we
    # avoid that. (This seems to be one downside of bokeh when compared to plotly)
    df = (df.pivot_table(
        index=[REGION_NAME_COL, Columns.STAGE, Columns.COUNT_TYPE],
        columns=STRING_DATE_COL,
        values=value_col,
        aggfunc="first",
    ).reset_index().merge(
        geo_df[[REGION_NAME_COL, LONG_COL, LAT_COL]],
        how="inner",
        on=REGION_NAME_COL,
    ))

    # All three oclumns are just initial values; they'll change with the date slider
    df[value_col] = df[max_date_str]
    df[FAKE_DATE_COL] = max_date_str
    df[COLOR_COL] = np.where(df[value_col] > 0, df[value_col], "NaN")

    # Technically takes a df but we don't need the index
    bokeh_data_source = ColumnDataSource(
        {k: v.tolist()
         for k, v in df.to_dict(orient="series").items()})

    filters = [[
        GroupFilter(column_name=Columns.STAGE, group=stage.name),
        GroupFilter(column_name=Columns.COUNT_TYPE, group=count.name),
    ] for stage, count in stage_count_list]

    figures = []

    for subplot_index, (stage, count) in enumerate(stage_count_list):
        # fig = bplotting.figure()
        # ax: plt.Axes = fig.add_subplot(
        #     len(stage_list), len(count_list), subplot_index
        # )

        # # Add timestamp to top right axis
        # if subplot_index == 2:
        #     ax.text(
        #         1.25,  # Coords are arbitrary magic numbers
        #         1.23,
        #         f"Last updated {NOW_STR}",
        #         horizontalalignment="right",
        #         fontsize="small",
        #         transform=ax.transAxes,
        #     )

        view = CDSView(source=bokeh_data_source,
                       filters=filters[subplot_index])

        vmin = vmins[(stage.name, count.name)]
        vmax = vmaxs[(stage.name, count.name)]

        # Compute and set axes titles
        if stage is DiseaseStage.CONFIRMED:
            fig_stage_name = "Cases"
        elif stage is DiseaseStage.DEATH:
            fig_stage_name = "Deaths"
        else:
            raise ValueError

        fig_title_components: List[str] = []
        if subplot_title_prefix is not None:
            fig_title_components.append(subplot_title_prefix)

        fig_title_components.append(fig_stage_name)

        if count is Counting.PER_CAPITA:
            _per_cap_denom = pow10s_series[(stage.name, count.name)]
            fig_title_components.append(f"Per {_per_cap_denom:,d} people")
            formatter = PrintfTickFormatter(format=r"%2.3f")
            label_standoff = 12
            tooltip_fmt = "{0.000}"
        else:
            formatter = NumeralTickFormatter(format="0.0a")
            label_standoff = 10
            tooltip_fmt = "{0}"

        color_mapper = LogColorMapper(
            color_list,
            low=vmin,
            high=vmax,
            nan_color="#f2f2f2",
        )

        fig_title = " ".join(fig_title_components)

        if plot_aspect_ratio is None:
            if x_range is None or y_range is None:
                raise ValueError("Must provide both `x_range` and `y_range`" +
                                 " when `plot_aspect_ratio` is None")
            plot_aspect_ratio = (x_range[1] - x_range[0]) / (y_range[1] -
                                                             y_range[0])

        # Create figure object
        p = bplotting.figure(
            title=fig_title,
            title_location="above",
            tools=[
                HoverTool(
                    tooltips=[
                        ("Date", f"@{{{FAKE_DATE_COL}}}"),
                        ("State", f"@{{{REGION_NAME_COL}}}"),
                        ("Count", f"@{{{value_col}}}{tooltip_fmt}"),
                    ],
                    toggleable=False,
                ),
                PanTool(),
                BoxZoomTool(match_aspect=True),
                ZoomInTool(),
                ZoomOutTool(),
                ResetTool(),
            ],
            active_drag=None,
            aspect_ratio=plot_aspect_ratio,
            output_backend="webgl",
            lod_factor=4,
            lod_interval=400,
            lod_threshold=1000,
            lod_timeout=300,
        )

        p.xgrid.grid_line_color = None
        p.ygrid.grid_line_color = None
        # Finally, add the actual choropleth data we care about
        p.patches(
            LONG_COL,
            LAT_COL,
            source=bokeh_data_source,
            view=view,
            fill_color={
                "field": COLOR_COL,
                "transform": color_mapper
            },
            line_color="black",
            line_width=0.25,
            fill_alpha=1,
        )

        # Add evenly spaced ticks and their labels to the colorbar
        # First major, then minor
        # Adapted from https://stackoverflow.com/a/50314773
        bucket_size = (vmax / vmin)**(1 / n_cbar_buckets)
        tick_dist = bucket_size**n_buckets_btwn_major_ticks

        # Simple log scale math
        major_tick_locs = (
            vmin * (tick_dist**np.arange(0, n_cbar_major_ticks))
            # * (bucket_size ** 0.5) # Use this if centering ticks on buckets
        )
        # Get minor locs by linearly interpolating between major ticks
        minor_tick_locs = []
        for major_tick_index, this_major_tick in enumerate(
                major_tick_locs[:-1]):
            next_major_tick = major_tick_locs[major_tick_index + 1]

            # Get minor ticks as numbers in range [this_major_tick, next_major_tick]
            # and exclude the major ticks themselves (once we've used them to
            # compute the minor tick locs)
            minor_tick_locs.extend(
                np.linspace(
                    this_major_tick,
                    next_major_tick,
                    n_minor_ticks_btwn_major_ticks + 2,
                )[1:-1])

        color_bar = ColorBar(
            color_mapper=color_mapper,
            ticker=FixedTicker(ticks=major_tick_locs,
                               minor_ticks=minor_tick_locs),
            formatter=formatter,
            label_standoff=label_standoff,
            major_tick_out=0,
            major_tick_in=13,
            major_tick_line_color="white",
            major_tick_line_width=1,
            minor_tick_out=0,
            minor_tick_in=5,
            minor_tick_line_color="white",
            minor_tick_line_width=1,
            location=(0, 0),
            border_line_color=None,
            bar_line_color=None,
            orientation="vertical",
        )

        p.add_layout(color_bar, "right")
        p.hover.point_policy = "follow_mouse"

        # Bokeh axes (and most other things) are splattable
        p.axis.visible = False

        figures.append(p)

    # Make all figs pan and zoom together by setting their axes equal to each other
    # Also fix the plots' aspect ratios
    figs_iter = iter(np.ravel(figures))
    anchor_fig = next(figs_iter)

    if x_range is not None and y_range is not None:
        data_aspect_ratio = (x_range[1] - x_range[0]) / (y_range[1] -
                                                         y_range[0])
    else:
        data_aspect_ratio = plot_aspect_ratio

    if x_range is not None:
        anchor_fig.x_range = Range1d(
            *x_range,
            bounds="auto",
            min_interval=min_visible_y_range * data_aspect_ratio,
        )

    if y_range is not None:
        anchor_fig.y_range = Range1d(*y_range,
                                     bounds="auto",
                                     min_interval=min_visible_y_range)

    for fig in figs_iter:
        fig.x_range = anchor_fig.x_range
        fig.y_range = anchor_fig.y_range

    # 2x2 grid (for now)
    gp = gridplot(
        figures,
        ncols=len(count_list),
        sizing_mode="scale_both",
        toolbar_location="above",
    )
    plot_layout = [gp]

    # Ok, pause
    # Now we're going into a whole other thing: we're doing all the JS logic behind a
    # date slider that changes which date is shown on the graphs. The structure of the
    # data is one column per date, one row per location, and a few extra columns to
    # store the data the graph will use. When we adjust the date of the slider, we copy
    # the relevant column of the df into the columns the graphs are looking at.
    # That's the easy part; the hard part is handling the "play button" functionality,
    # whereby the user can click one button and the date slider will periodically
    # advance itself. That requires a fair bit of logic to schedule and cancel the
    # timers and make it all feel right.

    # Create unique ID for the JS playback info object for this plot (since it'll be on
    # the webpage with other plots, and their playback info isn't shared)
    _THIS_PLOT_ID = uuid.uuid4().hex

    __TIMER = "'timer'"
    __IS_ACTIVE = "'isActive'"
    __SELECTED_INDEX = "'selectedIndex'"
    __BASE_INTERVAL_MS = "'BASE_INTERVAL'"  # Time (in MS) btwn frames when speed==1
    __TIMER_START_DATE = "'startDate'"
    __TIMER_ELAPSED_TIME_MS = "'elapsedTimeMS'"
    __TIMER_ELAPSED_TIME_PROPORTION = "'elapsedTimeProportion'"
    __SPEEDS_KEY = "'SPEEDS'"
    __PLAYBACK_INFO = f"window._playbackInfo_{_THIS_PLOT_ID}"

    _PBI_TIMER = f"{__PLAYBACK_INFO}[{__TIMER}]"
    _PBI_IS_ACTIVE = f"{__PLAYBACK_INFO}[{__IS_ACTIVE}]"
    _PBI_SELECTED_INDEX = f"{__PLAYBACK_INFO}[{__SELECTED_INDEX}]"
    _PBI_TIMER_START_DATE = f"{__PLAYBACK_INFO}[{__TIMER_START_DATE}]"
    _PBI_TIMER_ELAPSED_TIME_MS = f"{__PLAYBACK_INFO}[{__TIMER_ELAPSED_TIME_MS}]"
    _PBI_TIMER_ELAPSED_TIME_PROPORTION = (
        f"{__PLAYBACK_INFO}[{__TIMER_ELAPSED_TIME_PROPORTION}]")
    _PBI_BASE_INTERVAL = f"{__PLAYBACK_INFO}[{__BASE_INTERVAL_MS}]"
    _PBI_SPEEDS = f"{__PLAYBACK_INFO}[{__SPEEDS_KEY}]"
    _PBI_CURR_INTERVAL_MS = (
        f"{_PBI_BASE_INTERVAL} / {_PBI_SPEEDS}[{_PBI_SELECTED_INDEX}]")

    _SPEED_OPTIONS = [0.25, 0.5, 1.0, 2.0]
    _DEFAULT_SPEED = 1.0
    _DEFAULT_SELECTED_INDEX = _SPEED_OPTIONS.index(_DEFAULT_SPEED)

    _SETUP_WINDOW_PLAYBACK_INFO = f"""
        if (typeof({__PLAYBACK_INFO}) === 'undefined') {{
            {__PLAYBACK_INFO} = {{
                {__TIMER}: null,
                {__IS_ACTIVE}: false,
                {__SELECTED_INDEX}: {_DEFAULT_SELECTED_INDEX},
                {__TIMER_START_DATE}: null,
                {__TIMER_ELAPSED_TIME_MS}: 0,
                {__TIMER_ELAPSED_TIME_PROPORTION}: 0,
                {__BASE_INTERVAL_MS}: 1000,
                {__SPEEDS_KEY}: {_SPEED_OPTIONS}
            }};
        }}

    """

    _DEFFUN_INCR_DATE = f"""
        // See this link for why this works (it's an undocumented feature?)
        // https://discourse.bokeh.org/t/5254
        // Tl;dr we need this to automatically update the hover as the play button plays
        // Without this, the hover tooltip only updates when we jiggle the mouse
        // slightly

        let prev_val = null;
        source.inspect.connect(v => prev_val = v);

        function updateDate() {{
            {_PBI_TIMER_START_DATE} = new Date();
            {_PBI_TIMER_ELAPSED_TIME_MS} = 0
            if (dateSlider.value < maxDate) {{
                dateSlider.value += 86400000;
            }}

            if (dateSlider.value >= maxDate) {{
                console.log(dateSlider.value, maxDate)
                console.log('reached end')
                clearInterval({_PBI_TIMER});
                {_PBI_IS_ACTIVE} = false;
                playPauseButton.active = false;
                playPauseButton.change.emit();
                playPauseButton.label = 'Restart';
            }}

            dateSlider.change.emit();

            // This is pt. 2 of the prev_val/inspect stuff above
            if (prev_val !== null) {{
                source.inspect.emit(prev_val);
            }}
        }}
    """

    _DO_START_TIMER = f"""
        function startLoopTimer() {{
            updateDate();
            if ({_PBI_IS_ACTIVE}) {{
                {_PBI_TIMER} = setInterval(updateDate, {_PBI_CURR_INTERVAL_MS})
            }}

        }}

        {_PBI_TIMER_START_DATE} = new Date();

        // Should never be <0 or >1 but I am being very defensive here
        const proportionRemaining = 1 - (
            {_PBI_TIMER_ELAPSED_TIME_PROPORTION} <= 0
            ? 0
            : {_PBI_TIMER_ELAPSED_TIME_PROPORTION} >= 1
            ? 1
            : {_PBI_TIMER_ELAPSED_TIME_PROPORTION}
        );
        const remainingTimeMS = (
            {_PBI_CURR_INTERVAL_MS} * proportionRemaining
        );
        const initialInterval = (
            {_PBI_TIMER_ELAPSED_TIME_MS} === 0
            ? 0
            : remainingTimeMS
        );

        {_PBI_TIMER} = setTimeout(
            startLoopTimer,
            initialInterval
        );
    """

    _DO_STOP_TIMER = f"""
        const now = new Date();
        {_PBI_TIMER_ELAPSED_TIME_MS} += (
            now.getTime() - {_PBI_TIMER_START_DATE}.getTime()
        );
        {_PBI_TIMER_ELAPSED_TIME_PROPORTION} = (
            {_PBI_TIMER_ELAPSED_TIME_MS} / {_PBI_CURR_INTERVAL_MS}
        );
        clearInterval({_PBI_TIMER});
    """

    update_on_date_change_callback = CustomJS(
        args={"source": bokeh_data_source},
        code=f"""

        {_SETUP_WINDOW_PLAYBACK_INFO}

        const sliderValue = cb_obj.value;
        const sliderDate = new Date(sliderValue)
        // Ugh, actually requiring the date to be YYYY-MM-DD (matching DATE_FMT)
        const dateStr = sliderDate.toISOString().split('T')[0]

        const data = source.data;

        {_PBI_TIMER_ELAPSED_TIME_MS} = 0

        if (typeof(data[dateStr]) !== 'undefined') {{
            data['{value_col}'] = data[dateStr]

            const valueCol = data['{value_col}'];
            const colorCol = data['{COLOR_COL}'];
            const fakeDateCol = data['{FAKE_DATE_COL}']

            for (var i = 0; i < data['{value_col}'].length; i++) {{
                const value = valueCol[i]
                if (value == 0) {{
                    colorCol[i] = 'NaN';
                }} else {{
                    colorCol[i] = value;
                }}

                fakeDateCol[i] = dateStr;
            }}

            source.change.emit();

        }}

        """,
    )

    # Taking day-over-day diffs means the min slider day is one more than the min data
    # date (might be off by 1 if not using day over diffs but in practice not an issue)
    min_slider_date = min_date + pd.Timedelta(days=1)
    date_slider = DateSlider(
        start=min_slider_date,
        end=max_date,
        value=max_date,
        step=1,
        sizing_mode="stretch_width",
        width_policy="fit",
    )
    date_slider.js_on_change("value", update_on_date_change_callback)

    play_pause_button = Toggle(
        label="Start playing",
        button_type="success",
        active=False,
        sizing_mode="stretch_width",
    )

    animate_playback_callback = CustomJS(
        args={
            "source": bokeh_data_source,
            "dateSlider": date_slider,
            "playPauseButton": play_pause_button,
            "maxDate": max_date,
            "minDate": min_slider_date,
        },
        code=f"""

        {_SETUP_WINDOW_PLAYBACK_INFO}
        {_DEFFUN_INCR_DATE}

        if (dateSlider.value >= maxDate) {{
            if (playPauseButton.active) {{
                dateSlider.value = minDate;
                dateSlider.change.emit();

                // Hack to get timer to wait after date slider wraps; any positive
                // number works but the smaller the better
                {_PBI_TIMER_ELAPSED_TIME_MS} = 1;
            }}
        }}

        const active = cb_obj.active;
        {_PBI_IS_ACTIVE} = active;

        if (active) {{
            playPauseButton.label = 'Playing – Click/tap to pause'
            {_DO_START_TIMER}
        }} else {{
            playPauseButton.label = 'Paused – Click/tap to play'
            {_DO_STOP_TIMER}
        }}

        """,
    )

    play_pause_button.js_on_click(animate_playback_callback)

    change_playback_speed_callback = CustomJS(
        args={
            "source": bokeh_data_source,
            "dateSlider": date_slider,
            "playPauseButton": play_pause_button,
            "maxDate": max_date,
        },
        code=f"""

        {_SETUP_WINDOW_PLAYBACK_INFO}
        {_DEFFUN_INCR_DATE}

        // Must stop timer before handling changing the speed, as stopping the timer
        // saves values based on the current (unchaged) speed selection
        if ({_PBI_TIMER} !== null) {{
            {_DO_STOP_TIMER}
        }}

        const selectedIndex = cb_obj.active;
        {_PBI_SELECTED_INDEX} = selectedIndex;

        if ({_PBI_IS_ACTIVE}) {{
            {_DO_START_TIMER}
        }} else {{
            {_PBI_TIMER_ELAPSED_TIME_MS} = 0
        }}

        console.log({__PLAYBACK_INFO})

    """,
    )

    playback_speed_radio = RadioButtonGroup(
        labels=[f"{speed:.2g}x speed" for speed in _SPEED_OPTIONS],
        active=_DEFAULT_SELECTED_INDEX,
        sizing_mode="stretch_width",
    )
    playback_speed_radio.js_on_click(change_playback_speed_callback)

    plot_layout.append(
        layout_column(
            [
                date_slider,
                layout_row(
                    [play_pause_button, playback_speed_radio],
                    height_policy="min",
                ),
            ],
            width_policy="fit",
            height_policy="min",
        ))
    plot_layout = layout_column(plot_layout, sizing_mode="scale_both")

    # grid = gridplot(figures, ncols=len(count_list), sizing_mode="stretch_both")

    # Create the autoloading bokeh plot info (HTML + JS)
    js_path = str(Path(out_file_basename + "_autoload").with_suffix(".js"))
    tag_html_path = str(
        Path(out_file_basename + "_div_tag").with_suffix(".html"))

    js_code, tag_code = autoload_static(plot_layout, CDN, js_path)
    tag_uuid = re.search(r'id="([^"]+)"', tag_code).group(1)
    tag_code = re.sub(r'src="([^"]+)"', f'src="\\1?uuid={tag_uuid}"', tag_code)

    with open(Paths.DOCS / js_path,
              "w") as f_js, open(Paths.DOCS / tag_html_path, "w") as f_html:
        f_js.write(js_code)
        f_html.write(tag_code)

    # Create the video by creating stills of the graphs for each date and then stitching
    # the images into a video
    if should_make_video:
        save_dir: Path = PNG_SAVE_ROOT_DIR / out_file_basename
        save_dir.mkdir(parents=True, exist_ok=True)

        STILL_WIDTH = 1500
        STILL_HEIGHT = int(np.ceil(STILL_WIDTH / plot_aspect_ratio) *
                           1.05)  # Unclear why *1.05 is necessary
        gp.height = STILL_HEIGHT
        gp.width = STILL_WIDTH
        gp.sizing_mode = "fixed"
        orig_title = anchor_fig.title.text

        for date in dates:
            date_str = date.strftime(DATE_FMT)
            anchor_fig.title = Title(text=f"{orig_title} {date_str}")

            for p in figures:
                p.title = Title(text=p.title.text, text_font_size="20px")

            # Just a reimplementation of the JS code in the date slider's callback
            data = bokeh_data_source.data
            data[value_col] = data[date_str]

            for i, value in enumerate(data[value_col]):
                if value == 0:
                    data[COLOR_COL][i] = "NaN"
                else:
                    data[COLOR_COL][i] = value

                data[FAKE_DATE_COL][i] = date_str

            save_path: Path = (save_dir / date_str).with_suffix(".png")
            export_png(gp, filename=save_path)
            resize_to_even_dims(save_path, pad_bottom=0.08)

            if date == max(dates):
                poster_path: Path = (
                    PNG_SAVE_ROOT_DIR /
                    (out_file_basename + "_poster")).with_suffix(".png")
                poster_path.write_bytes(save_path.read_bytes())

        make_video(save_dir, out_file_basename, 0.9)

    print(f"Did interactive {out_file_basename}")

    return (js_code, tag_code)
Beispiel #14
0
v0_input_x.on_change('value', particle_speed_x)

## Create slider to select v0-y
v0_input_y = Slider(title=u"v\u2092-y",
                    value=-2.0,
                    start=-5.0,
                    end=5.0,
                    step=0.5)
v0_input_y.on_change('value', particle_speed_y)

## Create reset button
reset_button = Button(label="Reset", button_type="success")
reset_button.on_click(reset_situation)

## Create pause button
pause_button = Toggle(label="Pause", button_type="success")
pause_button.on_click(pause)

## Create re-initialise button
reinit_button = Button(label="Re-initialise", button_type="success")
reinit_button.on_click(BackToInitial)

## Create play button
play_button = Button(label="Play", button_type="success")
play_button.on_click(play)

## Create choice of referential button
Referential_button = RadioGroup(
    labels=["Reference frame: Room", "Reference frame: Disk"], active=0)
Referential_button.on_change('active', chooseRef)
    CheckboxButtonGroup,
    DatePicker,
    Dropdown,
    RadioButtonGroup,
    TextInput,
    Toggle,
)
from bokeh.plotting import save

menu = [("Item 1", "1"), ("Item 2", "2"), ("Item 3", "3")]

layout = column(
    Button(label="Default Button 1", button_type="default"),
    Button(label="Primary Button 2", button_type="primary"),
    Button(label="Success Button 3", button_type="success"),
    Toggle(label="Default Toggle 1", button_type="default"),
    Toggle(label="Primary Toggle 2", button_type="primary"),
    Toggle(label="Success Toggle 3", button_type="success"),
    Dropdown(label="Default Dropdown 1", button_type="default", menu=menu),
    Dropdown(label="Primary Dropdown 2", button_type="primary", menu=menu),
    Dropdown(label="Success Dropdown 3", button_type="success", menu=menu),
    CheckboxButtonGroup(
        labels=["Checkbox Option 1", "Checkbox Option 2", "Checkbox Option 3"],
        button_type="default",
        active=[0, 1]),
    CheckboxButtonGroup(
        labels=["Checkbox Option 4", "Checkbox Option 5", "Checkbox Option 6"],
        button_type="primary",
        active=[1, 2]),
    CheckboxButtonGroup(
        labels=["Checkbox Option 7", "Checkbox Option 8", "Checkbox Option 9"],
Beispiel #16
0
def plotHistogram(fileName, initData, stations, dateRange, bokehPlaceholderId='bokehContent'):
    data = {'xs':[initData['bins']], 'ys':[initData['values']],'ss':[1,2], 'es':[3,4] }#ss and es are for test purposes we'll add  other values of the controlles e.g. age, usertype, Gender coming fetshed from initdata 

    source = ColumnDataSource(data=data)
    stations.insert(0, "All")
    selectSS = Select(title="Start Station:", value="All", options=stations)
    selectES = Select(title="End Station:", value="All", options=stations)
    
    selectUT = Select(title="User Type:", value="All", options=["All", "Subscriber", "Customer"])
    selectGender = Select(title="Gender:", value="All", options=["All", "Male", "Female"])
    sliderAge = Slider(start=8, end=100, value=30, step=5, title="Age")    
    
    startDP = DatePicker(title="Start Date:", min_date=dateRange[0] ,max_date=dateRange[1], value=dateRange[0])
    endDP = DatePicker(title="End Date:", min_date=dateRange[0] ,max_date=dateRange[1], value=dateRange[1])
    binSize = TextInput(value="15", title="Bin Size (Days):")
    AddButton = Toggle(label="Add", type="success")
    DeleteButton = Toggle(label="delete", type="success")
    
    
    columns = [TableColumn(field="ss", title="Start Station"),TableColumn(field="es", title="End Station")]# add other columns contains values of other controllers
    data_table = DataTable(source=source, columns=columns, width=650, height=300)
    
    model = dict(source=source, selectSS = selectSS, selectES = selectES, startDP = startDP, endDP = endDP, binSize = binSize,selectUT=selectUT,selectGender=selectGender,sliderAge=sliderAge)
    plot = Figure(plot_width=650, plot_height=400, x_axis_type="datetime")
    plot.multi_line('xs', 'ys', source=source, line_width='width', line_alpha=0.6, line_color='color')
    
    callback = CustomJS(args=model, code="""
            //alert("callback");
            var startStation = selectSS.get('value');
            var endStation = selectES.get('value');
            var startDate = startDP.get('value');
            
            if ( typeof(startDate) !== "number")
                startDate = startDate.getTime();
                
            var endDate = endDP.get('value');
            
            if ( typeof(endDate) !== "number")
                endDate = endDate.getTime();            
            
            var binSize = binSize.get('value');
            //alert(startStation + " " + endStation + " " + startDate + " " + endDate + " " + binSize);
            var xmlhttp;
            xmlhttp = new XMLHttpRequest();
            
            xmlhttp.onreadystatechange = function() {
                if (xmlhttp.readyState == XMLHttpRequest.DONE ) {
                    if(xmlhttp.status == 200){
                        var data = source.get('data');
                        var result = JSON.parse(xmlhttp.responseText);
                        var temp=[];
                        
                        for(var date in result.x) {
                            temp.push(new Date(result.x[date]));
                        }
                        
                        data['xs'].push(temp);
                        data['ys'].push(result.y);
                        source.trigger('change');
                    }
                    else if(xmlhttp.status == 400) {
                        alert(400);
                    }
                    else {
                        alert(xmlhttp.status);
                    }
                }
            };
        var params = {ss:startStation, es:endStation, sd:startDate, ed:endDate, bs: binSize};
        url = "/histogram?" + jQuery.param( params );
        xmlhttp.open("GET", url, true);
        xmlhttp.send();
        """)
        
    
    AddButton.callback = callback
    #DeleteButton.on_click(callback1)
    layout1 = vform (startDP,endDP,binSize)
    layout2 = vform(plot,DeleteButton,data_table)
    layout3 = vform(selectSS, selectES,selectUT,selectGender,sliderAge,AddButton)
    layout = hplot(layout1,layout2,layout3)
    script, div = components(layout)
    html = readHtmlFile(fileName)
    html = insertScriptIntoHeader(html, script)
    html = appendElementContent(html, div, "div", "bokehContent")

    return html  
Beispiel #17
0
def widgets():
    from bokeh.io import show
    from bokeh.models import Select, CheckboxButtonGroup, Button, CheckboxGroup, ColorPicker, Dropdown, \
        FileInput, MultiSelect, RadioButtonGroup, RadioGroup, Slider, RangeSlider, TextAreaInput, TextInput, Toggle, \
        Paragraph, PreText, Div

    put_text('Button')
    button = Button(label="Foo", button_type="success")
    show(button)

    put_text('CheckboxButtonGroup')
    checkbox_button_group = CheckboxButtonGroup(
        labels=["Option 1", "Option 2", "Option 3"], active=[0, 1])
    show(checkbox_button_group)

    put_text('CheckboxGroup')
    checkbox_group = CheckboxGroup(labels=["Option 1", "Option 2", "Option 3"],
                                   active=[0, 1])
    show(checkbox_group)

    put_text('ColorPicker')
    color_picker = ColorPicker(color="#ff4466",
                               title="Choose color:",
                               width=200)
    show(color_picker)

    put_text('Dropdown')
    menu = [("Item 1", "item_1"), ("Item 2", "item_2"), None,
            ("Item 3", "item_3")]
    dropdown = Dropdown(label="Dropdown button",
                        button_type="warning",
                        menu=menu)
    show(dropdown)

    put_text('FileInput')
    file_input = FileInput()
    show(file_input)

    put_text('MultiSelect')
    multi_select = MultiSelect(title="Option:",
                               value=["foo", "quux"],
                               options=[("foo", "Foo"), ("bar", "BAR"),
                                        ("baz", "bAz"), ("quux", "quux")])
    show(multi_select)

    put_text('RadioButtonGroup')
    radio_button_group = RadioButtonGroup(
        labels=["Option 1", "Option 2", "Option 3"], active=0)
    show(radio_button_group)

    put_text('RadioGroup')
    radio_group = RadioGroup(labels=["Option 1", "Option 2", "Option 3"],
                             active=0)
    show(radio_group)

    put_text('Select')
    select = Select(title="Option:",
                    value="foo",
                    options=["foo", "bar", "baz", "quux"])
    show(select)

    put_text('Slider')
    slider = Slider(start=0, end=10, value=1, step=.1, title="Stuff")
    show(slider)

    put_text('RangeSlider')
    range_slider = RangeSlider(start=0,
                               end=10,
                               value=(1, 9),
                               step=.1,
                               title="Stuff")
    show(range_slider)

    put_text('TextAreaInput')
    text_input = TextAreaInput(value="default", rows=6, title="Label:")
    show(text_input)

    put_text('TextInput')
    text_input = TextInput(value="default", title="Label:")
    show(text_input)

    put_text('Toggle')
    toggle = Toggle(label="Foo", button_type="success")
    show(toggle)

    put_text('Div')
    div = Div(
        text=
        """Your <a href="https://en.wikipedia.org/wiki/HTML">HTML</a>-supported text is initialized with the <b>text</b> argument.  The
    remaining div arguments are <b>width</b> and <b>height</b>. For this example, those values
    are <i>200</i> and <i>100</i> respectively.""",
        width=200,
        height=100)
    show(div)

    put_text('Paragraph')
    p = Paragraph(
        text="""Your text is initialized with the 'text' argument.  The
    remaining Paragraph arguments are 'width' and 'height'. For this example, those values
    are 200 and 100 respectively.""",
        width=200,
        height=100)
    show(p)

    put_text('PreText')
    pre = PreText(text="""Your text is initialized with the 'text' argument.

    The remaining Paragraph arguments are 'width' and 'height'. For this example,
    those values are 500 and 100 respectively.""",
                  width=500,
                  height=100)
    show(pre)
Beispiel #18
0
def plotHistogram(fileName, initData, stations, dateRange, bokehPlaceholderId='bokehContent'):   
    MAX_AGE = 150
    #The initial data
    data = {
    'xs':[initData['bins']], 
    'ys':[initData['values']],
    'ss':["All"], 
    'es':["All"],  
    'ut':["All"], 
    'g':["All"], 
    'a':[MAX_AGE], 
    'color':["blue"],
    'line_width':[2]
    }

    source = ColumnDataSource(data=data)
    stations.insert(0, "All")
    selectSS = Select(title="Start Station:", value="All", options=stations)
    selectES = Select(title="End Station:", value="All", options=stations)
    selectUT = Select(title="User Type:", value="All", options=["All", "Subscriber", "Customer"])
    selectGender = Select(title="Gender:", value="All", options=["All", "Male", "Female", "Unknown"])
    sliderAge = Slider(start=0, end=MAX_AGE, value=0, step=1, title="Age LEQ")    
    startDP = DatePicker(title="Start Date:", min_date=dateRange[0] ,max_date=dateRange[1], value=dateRange[0])
    endDP = DatePicker(title="End Date:", min_date=dateRange[0] ,max_date=dateRange[1], value=dateRange[1])
    binSize = TextInput(value="15", title="Bin Size (Days):")
    AddButton = Toggle(label="Add", type="success")
    DeleteButton = Toggle(label="Delete Selected", type="success")
    
    columns = [
    TableColumn(field="ss", title="SS"), 
    TableColumn(field="es", title="ES"),
    TableColumn(field="ut", title="User Type"),
    TableColumn(field="a", title="Age LEQ"),    
    TableColumn(field="g", title="Sex")
    ]
    data_table = DataTable(source=source, columns=columns, width=400, row_headers=False, selectable='checkbox')
    model = dict(source=source, selectSS = selectSS, selectES = selectES, startDP = startDP, endDP = endDP, binSize = binSize,selectUT=selectUT,selectGender=selectGender,sliderAge=sliderAge, dt = data_table)
    
    addCallback = CustomJS(args=model, code="""
        //alert("callback");
        var startStation = selectSS.get('value');
        var endStation = selectES.get('value');
        var startDate = startDP.get('value');
        
        if ( typeof(startDate) !== "number")
            startDate = startDate.getTime();
            
        var endDate = endDP.get('value');
        
        if ( typeof(endDate) !== "number")
            endDate = endDate.getTime();            
        
        var binSize = binSize.get('value');
        var gender = selectGender.get('value');
        var userType = selectUT.get('value');
        var age = sliderAge.get('value');
        //alert(age);
        //alert(startStation + " " + endStation + " " + startDate + " " + endDate + " " + binSize);
        var xmlhttp;
        xmlhttp = new XMLHttpRequest();
        
        xmlhttp.onreadystatechange = function() {
            if (xmlhttp.readyState == XMLHttpRequest.DONE ) {
                if(xmlhttp.status == 200){
                    var data = source.get('data');
                    var result = JSON.parse(xmlhttp.responseText);
                    var temp=[];
                    
                    for(var date in result.x) {
                        temp.push(new Date(result.x[date]));
                    }
                    
                    data['xs'].push(temp);
                    data['ys'].push(result.y);
                    data['ss'].push(startStation);
                    data['es'].push(endStation);
                    data['ut'].push(userType);
                    data['g'].push(gender);
                    data['a'].push(age);
                    data['color'].push('blue');
                    data['line_width'].push(2);
                    source.trigger('change');
                    dt.trigger('change');
                }
                else if(xmlhttp.status == 400) {
                    alert(400);
                }
                else {
                    alert(xmlhttp.status);
                }
            }
        };
    var params = {ss:startStation, es:endStation, sd:startDate, ed:endDate, bs: binSize, g:gender, ut:userType, age:age};
    url = "/histogram?" + jQuery.param( params );
    xmlhttp.open("GET", url, true);
    xmlhttp.send();
    """)
    
    deleteCallBack = CustomJS(args=dict(source=source, dt= data_table), code="""
            var indices = source.get('selected')['1d'].indices;
            
            if(indices.length != 0){
                indices.sort();
                var data = source.get('data');
                var counter = 0;
                var i = 0;
                var key = 0;
                var index = 0;
                
                for(i in indices)
                {
                    index = indices[i];
                    index -= counter;
                    
                    for(key in data) {
                        data[key].splice(index, 1);
                    }
                    
                    counter += 1;
                }
                
                source.trigger('change');
                dt.trigger('change');
            }
            """)
    AddButton.callback = addCallback         
    DeleteButton.callback = deleteCallBack;
    
    plot = Figure(title="Number Of Trips Over Time", x_axis_label='Time', y_axis_label='Number of trips', plot_width=750, plot_height=400, x_axis_type="datetime")
    plot.multi_line('xs', 'ys', source=source, line_width='line_width', line_alpha=0.9, line_color='color')
    
    l2 = vform(plot, hplot(startDP, endDP), binSize)
    l3 = vform(selectSS, selectES,selectUT,selectGender,sliderAge, hplot(AddButton, DeleteButton), data_table)
    layout = hplot(l2, l3)
    script, div = components(layout)
    html = readHtmlFile(fileName)
    html = insertScriptIntoHeader(html, script)
    html = appendElementContent(html, div, "div", "bokehContent")

    return html  
    title="Error Plots",
    tools="crosshair,pan,reset,save,wheel_zoom",
    x_range=[start_m - 0.5, end_m + 0.5],
    y_range=[0, 2000],
)
error_land_data = ColumnDataSource(data=dict(
    x=[m],
    y=[utils.compute_error(x, y, m)],
))
error_plot.line('x', 'y', source=error_land_data, color='red')

# Set up widgets
set_m = Slider(title='m', value=m, start=start_m, step=0.1, end=end_m)
animate_button = Button(label='► Play', button_type='primary')
cluttered_button = Toggle(label='Filter points',
                          button_type='primary',
                          width=200)
button_draw_error = Toggle(label='Draw Errors',
                           button_type='primary',
                           width=200)
reset_button = Button(label='Reset errors')


# Setup callbacks
def change_m(attr, old, new):
    new_m = set_m.value

    x_line = np.arange(-1, 5.1, 0.1)
    y_line = new_m * x_line

    line_data.data = dict(x=x_line, y=y_line)
Beispiel #20
0
def create(palm):
    connected = False
    current_message = None
    stream_t = 0

    doc = curdoc()

    # Streaked and reference waveforms plot
    waveform_plot = Plot(
        title=Title(text="eTOF waveforms"),
        x_range=DataRange1d(),
        y_range=DataRange1d(),
        plot_height=PLOT_CANVAS_HEIGHT,
        plot_width=PLOT_CANVAS_WIDTH,
        toolbar_location="right",
    )

    # ---- tools
    waveform_plot.toolbar.logo = None
    waveform_plot.add_tools(PanTool(), BoxZoomTool(), WheelZoomTool(),
                            ResetTool())

    # ---- axes
    waveform_plot.add_layout(LinearAxis(axis_label="Photon energy, eV"),
                             place="below")
    waveform_plot.add_layout(LinearAxis(axis_label="Intensity",
                                        major_label_orientation="vertical"),
                             place="left")

    # ---- grid lines
    waveform_plot.add_layout(Grid(dimension=0, ticker=BasicTicker()))
    waveform_plot.add_layout(Grid(dimension=1, ticker=BasicTicker()))

    # ---- line glyphs
    waveform_source = ColumnDataSource(
        dict(x_str=[], y_str=[], x_ref=[], y_ref=[]))
    waveform_ref_line = waveform_plot.add_glyph(
        waveform_source, Line(x="x_ref", y="y_ref", line_color="blue"))
    waveform_str_line = waveform_plot.add_glyph(
        waveform_source, Line(x="x_str", y="y_str", line_color="red"))

    # ---- legend
    waveform_plot.add_layout(
        Legend(items=[("reference",
                       [waveform_ref_line]), ("streaked",
                                              [waveform_str_line])]))
    waveform_plot.legend.click_policy = "hide"

    # Cross-correlation plot
    xcorr_plot = Plot(
        title=Title(text="Waveforms cross-correlation"),
        x_range=DataRange1d(),
        y_range=DataRange1d(),
        plot_height=PLOT_CANVAS_HEIGHT,
        plot_width=PLOT_CANVAS_WIDTH,
        toolbar_location="right",
    )

    # ---- tools
    xcorr_plot.toolbar.logo = None
    xcorr_plot.add_tools(PanTool(), BoxZoomTool(), WheelZoomTool(),
                         ResetTool())

    # ---- axes
    xcorr_plot.add_layout(LinearAxis(axis_label="Energy shift, eV"),
                          place="below")
    xcorr_plot.add_layout(LinearAxis(axis_label="Cross-correlation",
                                     major_label_orientation="vertical"),
                          place="left")

    # ---- grid lines
    xcorr_plot.add_layout(Grid(dimension=0, ticker=BasicTicker()))
    xcorr_plot.add_layout(Grid(dimension=1, ticker=BasicTicker()))

    # ---- line glyphs
    xcorr_source = ColumnDataSource(dict(lags=[], xcorr=[]))
    xcorr_plot.add_glyph(xcorr_source,
                         Line(x="lags", y="xcorr", line_color="purple"))

    # ---- vertical span
    xcorr_center_span = Span(location=0, dimension="height")
    xcorr_plot.add_layout(xcorr_center_span)

    # Delays plot
    pulse_delay_plot = Plot(
        title=Title(text="Pulse delays"),
        x_range=DataRange1d(),
        y_range=DataRange1d(),
        plot_height=PLOT_CANVAS_HEIGHT,
        plot_width=PLOT_CANVAS_WIDTH,
        toolbar_location="right",
    )

    # ---- tools
    pulse_delay_plot.toolbar.logo = None
    pulse_delay_plot.add_tools(PanTool(), BoxZoomTool(), WheelZoomTool(),
                               ResetTool())

    # ---- axes
    pulse_delay_plot.add_layout(LinearAxis(axis_label="Pulse number"),
                                place="below")
    pulse_delay_plot.add_layout(
        LinearAxis(axis_label="Pulse delay (uncalib), eV",
                   major_label_orientation="vertical"),
        place="left",
    )

    # ---- grid lines
    pulse_delay_plot.add_layout(Grid(dimension=0, ticker=BasicTicker()))
    pulse_delay_plot.add_layout(Grid(dimension=1, ticker=BasicTicker()))

    # ---- line glyphs
    pulse_delay_source = ColumnDataSource(dict(x=[], y=[]))
    pulse_delay_plot.add_glyph(pulse_delay_source,
                               Line(x="x", y="y", line_color="steelblue"))

    # Pulse lengths plot
    pulse_length_plot = Plot(
        title=Title(text="Pulse lengths"),
        x_range=DataRange1d(),
        y_range=DataRange1d(),
        plot_height=PLOT_CANVAS_HEIGHT,
        plot_width=PLOT_CANVAS_WIDTH,
        toolbar_location="right",
    )

    # ---- tools
    pulse_length_plot.toolbar.logo = None
    pulse_length_plot.add_tools(PanTool(), BoxZoomTool(), WheelZoomTool(),
                                ResetTool())

    # ---- axes
    pulse_length_plot.add_layout(LinearAxis(axis_label="Pulse number"),
                                 place="below")
    pulse_length_plot.add_layout(
        LinearAxis(axis_label="Pulse length (uncalib), eV",
                   major_label_orientation="vertical"),
        place="left",
    )

    # ---- grid lines
    pulse_length_plot.add_layout(Grid(dimension=0, ticker=BasicTicker()))
    pulse_length_plot.add_layout(Grid(dimension=1, ticker=BasicTicker()))

    # ---- line glyphs
    pulse_length_source = ColumnDataSource(dict(x=[], y=[]))
    pulse_length_plot.add_glyph(pulse_length_source,
                                Line(x="x", y="y", line_color="steelblue"))

    # Image buffer slider
    def buffer_slider_callback(_attr, _old, new):
        message = receiver.data_buffer[new]
        doc.add_next_tick_callback(partial(update, message=message))

    buffer_slider = Slider(
        start=0,
        end=59,
        value=0,
        step=1,
        title="Buffered Image",
        callback_policy="throttle",
        callback_throttle=500,
    )
    buffer_slider.on_change("value", buffer_slider_callback)

    # Connect toggle button
    def connect_toggle_callback(state):
        nonlocal connected
        if state:
            connected = True
            connect_toggle.label = "Connecting"
            connect_toggle.button_type = "default"

        else:
            connected = False
            connect_toggle.label = "Connect"
            connect_toggle.button_type = "default"

    connect_toggle = Toggle(label="Connect", button_type="default", width=250)
    connect_toggle.on_click(connect_toggle_callback)

    # Intensity stream reset button
    def reset_button_callback():
        nonlocal stream_t
        stream_t = 1  # keep the latest point in order to prevent full axis reset

    reset_button = Button(label="Reset", button_type="default", width=250)
    reset_button.on_click(reset_button_callback)

    # Stream update coroutine
    async def update(message):
        nonlocal stream_t
        if connected and receiver.state == "receiving":
            y_ref = message[receiver.reference].value[np.newaxis, :]
            y_str = message[receiver.streaked].value[np.newaxis, :]

            delay, length, debug_data = palm.process({
                "0": y_ref,
                "1": y_str
            },
                                                     debug=True)
            prep_data, lags, corr_res_uncut, _ = debug_data

            waveform_source.data.update(
                x_str=palm.energy_range,
                y_str=prep_data["1"][0, :],
                x_ref=palm.energy_range,
                y_ref=prep_data["0"][0, :],
            )

            xcorr_source.data.update(lags=lags, xcorr=corr_res_uncut[0, :])
            xcorr_center_span.location = delay[0]

            pulse_delay_source.stream({
                "x": [stream_t],
                "y": [delay]
            },
                                      rollover=120)
            pulse_length_source.stream({
                "x": [stream_t],
                "y": [length]
            },
                                       rollover=120)

            stream_t += 1

    # Periodic callback to fetch data from receiver
    async def internal_periodic_callback():
        nonlocal current_message
        if waveform_plot.inner_width is None:
            # wait for the initialization to finish, thus skip this periodic callback
            return

        if connected:
            if receiver.state == "polling":
                connect_toggle.label = "Polling"
                connect_toggle.button_type = "warning"

            elif receiver.state == "stopped":
                connect_toggle.label = "Not available"
                connect_toggle.button_type = "danger"

            elif receiver.state == "receiving":
                connect_toggle.label = "Receiving"
                connect_toggle.button_type = "success"

                # Set slider to the right-most position
                if len(receiver.data_buffer) > 1:
                    buffer_slider.end = len(receiver.data_buffer) - 1
                    buffer_slider.value = len(receiver.data_buffer) - 1

                if receiver.data_buffer:
                    current_message = receiver.data_buffer[-1]

        doc.add_next_tick_callback(partial(update, message=current_message))

    doc.add_periodic_callback(internal_periodic_callback, 1000)

    # assemble
    tab_layout = column(
        row(
            column(waveform_plot, xcorr_plot),
            Spacer(width=30),
            column(buffer_slider, row(connect_toggle, reset_button)),
        ),
        row(pulse_delay_plot, Spacer(width=10), pulse_length_plot),
    )

    return Panel(child=tab_layout, title="Stream")
buttons = {
    'restart': Button(label='Restart', align='center', width=165),
    'stop': Button(label='Stop',
                   align='center',
                   button_type='danger',
                   width=165)
}

buttons['restart'].on_click(restart)
buttons['stop'].js_on_click(CustomJS(code='window.close()'))
buttons['stop'].on_click(lambda: sys.exit())

toggles = {
    'pause':
    Toggle(label='Pause', align='center', button_type='success', width=165)
}

toggles['pause'].on_click(partial(callback_pause, ref=toggles['pause']))

selectors = {
    'activation': {
        'Activate': lambda x: (x**3 + 50 * x**2 + 10 * x) / 1000
    },
    'optimizer': {
        'Activate': optimizers.SGD()
    },
    'eta_modifier': {
        'Activate': 1
    }
}
# linear regression object
regr = linear_model.LinearRegression()

# fit linear model
regr.fit(X, Y)

# make predictions
pred = regr.predict(X)

# plot with regression line
regr_plot = figure(plot_width=500, plot_height=300)
regr_plot.scatter(x, y, size=10)
regr_line = regr_plot.line(x, pred.flatten(), line_color='red')

toggle_button = Toggle(label='line of best fit', button_type='success', active=True)
toggle_button.js_link('active', regr_line, 'visible')

show(layout([regr_plot], [toggle_button]))
Loading BokehJS ...
In [87]:
# slider.js_link?
Interactive Widgets with ipywidgets
In [88]:
seattle_weather['year'] = pd.DatetimeIndex(seattle_weather['date']).year
seattle_weather.tail()
Out[88]:
date	precipitation	temp_max	temp_min	wind	weather	year
1456	2015-12-27	8.6	4.4	1.7	2.9	fog	2015
1457	2015-12-28	1.5	5.0	1.7	1.3	fog	2015
1458	2015-12-29	0.0	7.2	0.6	2.6	fog	2015
Beispiel #23
0
    y_offset="y_off",
    text="text",
    text_font_size="64px",
    text_align="center",
)
grid.add_glyph(letter_src, letters)

# Set up widgets
time_slider = Slider(start=1,
                     end=10,
                     value=3,
                     step=1,
                     title="Duration (minutes)")
special_toggle = Toggle(
    label="""Include bonus die? \n
[He, An, In, Er, Th, Qu]""",
    button_type="warning",
)
angle_toggle = Toggle(label="Rotate dice in random direction?",
                      button_type="primary")
shuffle_button = Button(label="Shake!", button_type="primary")
start_button = Button(label="Start timer", button_type="success")
stop_button = Button(label="Stop timer", button_type="danger")
timer = Div(
    text=f"""Timer: <br> 0:00""",
    style={
        "font-size": "400%",
        "color": "black",
        "text-align": "center"
    },
)
Beispiel #24
0
def update():

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

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


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

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

# Import CheckboxGroup, RadioGroup, Toggle from bokeh.models
from bokeh.models import CheckboxGroup, RadioGroup, Toggle

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

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

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

# Add widgetbox(toggle, checkbox, radio) to the current document
curdoc().add_root(widgetbox(toggle, checkbox, radio))
# Setup color bar
mapper = LinearColorMapper(palette=colors, low=-1, high=1)
color_bar = ColorBar(color_mapper=mapper, location=(0, 0))
p3.add_layout(color_bar, 'right')

#Spinner GUI
spinner = Spinner(title="Size", low=0, high=4, step=0.1, value=1, width=80)
#spinner.js_link('value', points.glyph, 'radius')

#Dropdown menu GUI
menu = [("Item 1", "item_1"), ("Item 2", "item_2"), None, ("Item 3", "item_3")]
dropdown = Dropdown(label="Dropdown button", button_type="warning", menu=menu)
dropdown.js_on_event("menu_item_click", CustomJS(code="console.log('dropdown: ' + this.item, this.toString())"))

#Toggle button GUI
toggle = Toggle(label="Button", button_type="success")
toggle.js_on_click(CustomJS(code="""
    console.log('toggle: active=' + this.active, this.toString())
"""))
OPTIONS = ["Attribute1", "Attribute2", "Attribute3", "Attribute4"]
multi_choice = MultiChoice(value=["foo", "baz"], options=OPTIONS)
multi_choice.js_on_change("value", CustomJS(code="""
    console.log('multi_choice: value=' + this.value, this.toString())
"""))

#GUI Left column
controls = [dropdown, spinner, toggle]
inputs = column(*controls, sizing_mode='fixed', height=250, width=350)
l1 = layout([[inputs, p1]], sizing_mode='fixed', height=600, width=150)
l2 = layout([[inputs, p2]], sizing_mode='fixed', height=600, width=150)
l3 = layout([[inputs, p3]], sizing_mode='fixed', height=600, width=150)
Beispiel #26
0
def sky_plot(db_file, pointings):
    '''
    plot pointings in pulsar catalog pointed to by db_file.
    plot pointings in database retrieved in pointings parameter.

    THIS IS A DIRTY IMPLEMENTATION. WE SHOULD CLEAN THIS UP.
    '''
    raj, decj = load_psr_pos(db_file)

    source = ColumnDataSource(data=dict(x=raj, y=decj))

    # unpack pointings taken from database
    dates, ras, decs = zip(*pointings)

    cover_raj, cover_decj = get_sky_cover_area(ras, decs, 2.5 / 60.0)
    cover_raj *= np.pi / 180.
    cover_decj *= np.pi / 180.
    projects = ['P2030'] * len(cover_decj)
    source2 = ColumnDataSource(data=dict(
        x=cover_raj.tolist(),
        y=cover_decj.tolist(),
        project=projects,
        date=dates,
    ))
    TOOLS = "pan,wheel_zoom,reset,hover,hover,save"
    plot = figure(x_range=(-1, 7),
                  y_range=(-2, 2),
                  tools=TOOLS,
                  toolbar_location="left",
                  title="ANTF Catalogue",
                  x_axis_label='RAJ (rad)',
                  y_axis_label='DECJ (rad)')

    box_color = "navy"
    box_alpha = 0.4
    starplot = plot.asterisk('x', 'y', source=source, size=5)
    cover_box = plot.patches('x',
                             'y',
                             source=source2,
                             line_width=3,
                             color=box_color,
                             fill_alpha=box_alpha)

    pulsar_tooltips = [
        ("RA, DEC : ", "($x, $y)"),
    ]

    obs_tooltips = [
        ("Project", "@project"),
        ("Date", "@date"),
        ("Time", "@time"),
        ("Observer", "@name"),
        ("RA, DEC : ", "($x, $y)"),
    ]

    hover = plot.select(dict(type=HoverTool))
    hover[0].renderers = [starplot]
    hover[0].tooltips = pulsar_tooltips
    hover[1].renderers = [cover_box]
    hover[1].tooltips = obs_tooltips

    code2 = '''\
    object.visible = toggle.active
    '''

    callback_star = CustomJS.from_coffeescript(code=code2, args={})
    toggle1 = Toggle(label="Pulsars",
                     button_type="success",
                     callback=callback_star)
    callback_star.args = {'toggle': toggle1, 'object': starplot}

    callback_box = CustomJS.from_coffeescript(code=code2, args={})
    toggle2 = Toggle(label="Observation Scans",
                     button_type="success",
                     callback=callback_box)
    callback_box.args = {'toggle': toggle2, 'object': cover_box}
    return [plot, toggle1, toggle2]
Beispiel #27
0
class Dashboard:
    """Explorepy dashboard class"""
    def __init__(self, explore=None, mode='signal'):
        """
        Args:
            stream_processor (explorepy.stream_processor.StreamProcessor): Stream processor object
        """
        self.explore = explore
        self.stream_processor = self.explore.stream_processor
        self.n_chan = self.stream_processor.device_info['adc_mask'].count(1)
        self.y_unit = DEFAULT_SCALE
        self.offsets = np.arange(1, self.n_chan + 1)[:,
                                                     np.newaxis].astype(float)
        self.chan_key_list = [
            CHAN_LIST[i] for i, mask in enumerate(
                reversed(self.stream_processor.device_info['adc_mask']))
            if mask == 1
        ]
        self.exg_mode = 'EEG'
        self.rr_estimator = None
        self.win_length = WIN_LENGTH
        self.mode = mode
        self.exg_fs = self.stream_processor.device_info['sampling_rate']

        # Init ExG data source
        exg_temp = np.zeros((self.n_chan, 2))
        exg_temp[:, 0] = self.offsets[:, 0]
        exg_temp[:, 1] = np.nan
        init_data = dict(zip(self.chan_key_list, exg_temp))
        self._exg_source_orig = ColumnDataSource(data=init_data)
        init_data['t'] = np.array([0., 0.])
        self._exg_source_ds = ColumnDataSource(
            data=init_data)  # Downsampled ExG data for visualization purposes

        # Init ECG R-peak source
        init_data = dict(
            zip(['r_peak', 't'], [
                np.array([None], dtype=np.double),
                np.array([None], dtype=np.double)
            ]))
        self._r_peak_source = ColumnDataSource(data=init_data)

        # Init marker source
        init_data = dict(
            zip(['marker', 't'], [
                np.array([None], dtype=np.double),
                np.array([None], dtype=np.double)
            ]))
        self._marker_source = ColumnDataSource(data=init_data)

        # Init ORN data source
        init_data = dict(zip(ORN_LIST, np.zeros((9, 1))))
        init_data['t'] = [0.]
        self._orn_source = ColumnDataSource(data=init_data)

        # Init table sources
        self._heart_rate_source = ColumnDataSource(data={'heart_rate': ['NA']})
        self._firmware_source = ColumnDataSource(
            data={
                'firmware_version':
                [self.stream_processor.device_info['firmware_version']]
            })
        self._battery_source = ColumnDataSource(data={'battery': ['NA']})
        self.temperature_source = ColumnDataSource(
            data={'temperature': ['NA']})
        self.light_source = ColumnDataSource(data={'light': ['NA']})
        self.battery_percent_list = []
        self.server = None

        # Init fft data source
        init_data = dict(zip(self.chan_key_list, np.zeros((self.n_chan, 1))))
        init_data['f'] = np.array([0.])
        self.fft_source = ColumnDataSource(data=init_data)

        # Init impedance measurement source
        init_data = {
            'channel': self.chan_key_list,
            'impedance': ['NA' for i in range(self.n_chan)],
            'row': ['1' for i in range(self.n_chan)],
            'color': ['black' for i in range(self.n_chan)]
        }
        self.imp_source = ColumnDataSource(data=init_data)

        # Init timer source
        self._timer_source = ColumnDataSource(data={'timer': ['00:00:00']})

    def start_server(self):
        """Start bokeh server"""
        validate(False)
        self.server = Server({'/': self._init_doc}, num_procs=1)
        self.server.start()

    def start_loop(self):
        """Start io loop and show the dashboard"""
        self.server.io_loop.add_callback(self.server.show, "/")
        self.server.io_loop.start()

    def exg_callback(self, packet):
        """
        Update ExG data in the visualization

        Args:
            packet (explorepy.packet.EEG): Received ExG packet

        """
        time_vector, exg = packet.get_data(self.exg_fs)
        self._exg_source_orig.stream(dict(zip(self.chan_key_list, exg)),
                                     rollover=int(self.exg_fs *
                                                  self.win_length))

        # Downsampling
        exg = exg[:, ::int(self.exg_fs / EXG_VIS_SRATE)]
        time_vector = time_vector[::int(self.exg_fs / EXG_VIS_SRATE)]
        # Update ExG unit
        exg = self.offsets + exg / self.y_unit
        new_data = dict(zip(self.chan_key_list, exg))
        new_data['t'] = time_vector
        self.doc.add_next_tick_callback(
            partial(self._update_exg, new_data=new_data))

    def orn_callback(self, packet):
        """Update orientation data

        Args:
            packet (explorepy.packet.Orientation): Orientation packet
        """
        if self.tabs.active != 1:
            return
        timestamp, orn_data = packet.get_data()
        new_data = dict(zip(ORN_LIST, np.array(orn_data)[:, np.newaxis]))
        new_data['t'] = timestamp
        self.doc.add_next_tick_callback(
            partial(self._update_orn, new_data=new_data))

    def info_callback(self, packet):
        """Update device information in the dashboard

        Args:
            packet (explorepy.packet.Environment): Environment/DeviceInfo packet

        """
        new_info = packet.get_data()
        for key in new_info.keys():
            data = {key: new_info[key]}
            if key == 'firmware_version':
                self.doc.add_next_tick_callback(
                    partial(self._update_fw_version, new_data=data))
            elif key == 'battery':
                self.battery_percent_list.append(new_info[key][0])
                if len(self.battery_percent_list) > BATTERY_N_MOVING_AVERAGE:
                    del self.battery_percent_list[0]
                value = int(np.mean(self.battery_percent_list) / 5) * 5
                if value < 1:
                    value = 1
                self.doc.add_next_tick_callback(
                    partial(self._update_battery, new_data={key: [value]}))
            elif key == 'temperature':
                self.doc.add_next_tick_callback(
                    partial(self._update_temperature, new_data=data))
            elif key == 'light':
                data[key] = [int(data[key][0])]
                self.doc.add_next_tick_callback(
                    partial(self._update_light, new_data=data))
            else:
                print("Warning: There is no field named: " + key)

    def marker_callback(self, packet):
        """Update markers
        Args:
            packet (explorepy.packet.EventMarker): Event marker packet
        """
        if self.mode == "impedance":
            return
        timestamp, _ = packet.get_data()
        new_data = dict(
            zip(['marker', 't', 'code'], [
                np.array([0.01, self.n_chan + 0.99, None], dtype=np.double),
                np.array([timestamp[0], timestamp[0], None], dtype=np.double)
            ]))
        self.doc.add_next_tick_callback(
            partial(self._update_marker, new_data=new_data))

    def impedance_callback(self, packet):
        """Update impedances

        Args:
             packet (explorepy.packet.EEG): ExG packet
        """
        if self.mode == "impedance":
            imp = packet.get_impedances()
            color = []
            imp_status = []
            for value in imp:
                if value > 500:
                    color.append("black")
                    imp_status.append("Open")
                elif value > 100:
                    color.append("red")
                    imp_status.append(str(round(value, 0)) + " K\u03A9")
                elif value > 50:
                    color.append("orange")
                    imp_status.append(str(round(value, 0)) + " K\u03A9")
                elif value > 10:
                    color.append("yellow")
                    imp_status.append(str(round(value, 0)) + " K\u03A9")
                elif value > 5:
                    imp_status.append(str(round(value, 0)) + " K\u03A9")
                    color.append("green")
                else:
                    color.append("green")
                    imp_status.append(
                        "<5K\u03A9"
                    )  # As the ADS is not precise in low values.

            data = {
                "impedance": imp_status,
                'channel': self.chan_key_list,
                'row': ['1' for i in range(self.n_chan)],
                'color': color
            }
            self.doc.add_next_tick_callback(
                partial(self._update_imp, new_data=data))
        else:
            raise RuntimeError(
                "Trying to compute impedances while the dashboard is not in Impedance mode!"
            )

    @gen.coroutine
    @without_property_validation
    def _update_exg(self, new_data):
        self._exg_source_ds.stream(new_data,
                                   rollover=int(2 * EXG_VIS_SRATE *
                                                WIN_LENGTH))

    @gen.coroutine
    @without_property_validation
    def _update_orn(self, new_data):
        self._orn_source.stream(new_data,
                                rollover=int(2 * WIN_LENGTH * ORN_SRATE))

    @gen.coroutine
    @without_property_validation
    def _update_fw_version(self, new_data):
        self._firmware_source.stream(new_data, rollover=1)

    @gen.coroutine
    @without_property_validation
    def _update_battery(self, new_data):
        self._battery_source.stream(new_data, rollover=1)

    @gen.coroutine
    @without_property_validation
    def _update_temperature(self, new_data):
        self.temperature_source.stream(new_data, rollover=1)

    @gen.coroutine
    @without_property_validation
    def _update_light(self, new_data):
        self.light_source.stream(new_data, rollover=1)

    @gen.coroutine
    @without_property_validation
    def _update_marker(self, new_data):
        self._marker_source.stream(new_data=new_data, rollover=100)

    @gen.coroutine
    @without_property_validation
    def _update_imp(self, new_data):
        self.imp_source.stream(new_data, rollover=self.n_chan)

    @gen.coroutine
    @without_property_validation
    def _update_fft(self):
        """ Update spectral frequency analysis plot"""
        # Check if the tab is active and if EEG mode is active
        if (self.tabs.active != 2) or (self.exg_mode != 'EEG'):
            return

        exg_data = np.array(
            [self._exg_source_orig.data[key] for key in self.chan_key_list])

        if exg_data.shape[1] < self.exg_fs * 5:
            return
        fft_content, freq = get_fft(exg_data, self.exg_fs)
        data = dict(zip(self.chan_key_list, fft_content))
        data['f'] = freq
        self.fft_source.data = data

    @gen.coroutine
    @without_property_validation
    def _update_heart_rate(self):
        """Detect R-peaks and update the plot and heart rate"""
        if self.exg_mode == 'EEG':
            self._heart_rate_source.stream({'heart_rate': ['NA']}, rollover=1)
            return
        if CHAN_LIST[0] not in self.chan_key_list:
            print(
                'WARNING: Heart rate estimation works only when channel 1 is enabled.'
            )
            return
        if self.rr_estimator is None:
            self.rr_estimator = HeartRateEstimator(fs=self.exg_fs)
            # Init R-peaks plot
            self.exg_plot.circle(x='t',
                                 y='r_peak',
                                 source=self._r_peak_source,
                                 fill_color="red",
                                 size=8)

        ecg_data = (
            np.array(self._exg_source_ds.data['Ch1'])[-2 * EXG_VIS_SRATE:] -
            self.offsets[0]) * self.y_unit
        time_vector = np.array(self._exg_source_ds.data['t'])[-2 *
                                                              EXG_VIS_SRATE:]

        # Check if the peak2peak value is bigger than threshold
        if (np.ptp(ecg_data) < V_TH[0]) or (np.ptp(ecg_data) > V_TH[1]):
            print(
                "WARNING: P2P value larger or less than threshold. Cannot compute heart rate!"
            )
            return

        peaks_time, peaks_val = self.rr_estimator.estimate(
            ecg_data, time_vector)
        peaks_val = (np.array(peaks_val) / self.y_unit) + self.offsets[0]
        if peaks_time:
            data = dict(zip(['r_peak', 't'], [peaks_val, peaks_time]))
            self._r_peak_source.stream(data, rollover=50)

        # Update heart rate cell
        estimated_heart_rate = self.rr_estimator.heart_rate
        data = {'heart_rate': [estimated_heart_rate]}
        self._heart_rate_source.stream(data, rollover=1)

    @gen.coroutine
    @without_property_validation
    def _change_scale(self, attr, old, new):
        """Change y-scale of ExG plot"""
        new, old = SCALE_MENU[new], SCALE_MENU[old]
        old_unit = 10**(-old)
        self.y_unit = 10**(-new)

        for chan, value in self._exg_source_ds.data.items():
            if chan in self.chan_key_list:
                temp_offset = self.offsets[self.chan_key_list.index(chan)]
                self._exg_source_ds.data[chan] = (value - temp_offset) * (
                    old_unit / self.y_unit) + temp_offset
        self._r_peak_source.data['r_peak'] = (np.array(self._r_peak_source.data['r_peak']) - self.offsets[0]) * \
                                             (old_unit / self.y_unit) + self.offsets[0]

    @gen.coroutine
    @without_property_validation
    def _change_t_range(self, attr, old, new):
        """Change time range"""
        self._set_t_range(TIME_RANGE_MENU[new])

    @gen.coroutine
    def _change_mode(self, attr, old, new):
        """Set EEG or ECG mode"""
        self.exg_mode = new

    def _init_doc(self, doc):
        self.doc = doc
        self.doc.title = "Explore Dashboard"
        with open(
                os.path.join(os.path.dirname(__file__), 'templates',
                             'index.html')) as f:
            index_template = Template(f.read())
        doc.template = index_template
        self.doc.theme = Theme(
            os.path.join(os.path.dirname(__file__), 'theme.yaml'))
        self._init_plots()
        m_widgetbox = self._init_controls()

        # Create tabs
        if self.mode == "signal":
            exg_tab = Panel(child=self.exg_plot, title="ExG Signal")
            orn_tab = Panel(child=column(
                [self.acc_plot, self.gyro_plot, self.mag_plot]),
                            title="Orientation")
            fft_tab = Panel(child=self.fft_plot, title="Spectral analysis")
            self.tabs = Tabs(tabs=[exg_tab, orn_tab, fft_tab], width=600)
            self.recorder_widget = self._init_recorder()
        elif self.mode == "impedance":
            imp_tab = Panel(child=self.imp_plot, title="Impedance")
            self.tabs = Tabs(tabs=[imp_tab], width=600)
        banner = Div(
            text=
            """<font size="5.5">Explorepy Dashboard</font> <a href="https://www.mentalab.co"><img src=
        "https://images.squarespace-cdn.com/content/5428308ae4b0701411ea8aaf/1505653866447-R24N86G5X1HFZCD7KBWS/
        Mentalab%2C+Name+copy.png?format=1500w&content-type=image%2Fpng" alt="Mentalab"  width="225" height="39">""",
            width=1500,
            height=50,
            css_classes=["banner"],
            align='center')

        if self.mode == 'signal':
            self.doc.add_root(
                column(
                    banner, Spacer(width=600, height=20),
                    row([
                        column(m_widgetbox, self.recorder_widget),
                        Spacer(width=25, height=500), self.tabs,
                        Spacer(width=700, height=600)
                    ])))
        elif self.mode == 'impedance':
            self.doc.add_root(
                column(
                    banner, Spacer(width=600, height=20),
                    row([m_widgetbox,
                         Spacer(width=25, height=500), self.tabs])))
        self.doc.add_periodic_callback(self._update_fft, 2000)
        self.doc.add_periodic_callback(self._update_heart_rate, 2000)
        if self.stream_processor:
            self.stream_processor.subscribe(topic=TOPICS.filtered_ExG,
                                            callback=self.exg_callback)
            self.stream_processor.subscribe(topic=TOPICS.raw_orn,
                                            callback=self.orn_callback)
            self.stream_processor.subscribe(topic=TOPICS.device_info,
                                            callback=self.info_callback)
            self.stream_processor.subscribe(topic=TOPICS.marker,
                                            callback=self.marker_callback)
            self.stream_processor.subscribe(topic=TOPICS.env,
                                            callback=self.info_callback)
            self.stream_processor.subscribe(topic=TOPICS.imp,
                                            callback=self.impedance_callback)

    def _init_plots(self):
        """Initialize all plots in the dashboard"""
        self.exg_plot = figure(y_range=(0.01, self.n_chan + 1 - 0.01),
                               y_axis_label='Voltage',
                               x_axis_label='Time (s)',
                               title="ExG signal",
                               plot_height=600,
                               plot_width=1270,
                               y_minor_ticks=int(10),
                               tools=[ResetTool()],
                               active_scroll=None,
                               active_drag=None,
                               active_inspect=None,
                               active_tap=None)

        self.mag_plot = figure(y_axis_label='Magnetometer [mgauss/LSB]',
                               x_axis_label='Time (s)',
                               plot_height=230,
                               plot_width=1270,
                               tools=[ResetTool()],
                               active_scroll=None,
                               active_drag=None,
                               active_inspect=None,
                               active_tap=None)
        self.acc_plot = figure(y_axis_label='Accelerometer [mg/LSB]',
                               plot_height=190,
                               plot_width=1270,
                               tools=[ResetTool()],
                               active_scroll=None,
                               active_drag=None,
                               active_inspect=None,
                               active_tap=None)
        self.acc_plot.xaxis.visible = False
        self.gyro_plot = figure(y_axis_label='Gyroscope [mdps/LSB]',
                                plot_height=190,
                                plot_width=1270,
                                tools=[ResetTool()],
                                active_scroll=None,
                                active_drag=None,
                                active_inspect=None,
                                active_tap=None)
        self.gyro_plot.xaxis.visible = False

        self.fft_plot = figure(y_axis_label='Amplitude (uV)',
                               x_axis_label='Frequency (Hz)',
                               title="FFT",
                               x_range=(0, 70),
                               plot_height=600,
                               plot_width=1270,
                               y_axis_type="log")

        self.imp_plot = self._init_imp_plot()

        # Set yaxis properties
        self.exg_plot.yaxis.ticker = SingleIntervalTicker(interval=1,
                                                          num_minor_ticks=0)

        # Initial plot line
        for i in range(self.n_chan):
            self.exg_plot.line(x='t',
                               y=self.chan_key_list[i],
                               source=self._exg_source_ds,
                               line_width=1.0,
                               alpha=.9,
                               line_color="#42C4F7")
            self.fft_plot.line(x='f',
                               y=self.chan_key_list[i],
                               source=self.fft_source,
                               legend_label=self.chan_key_list[i] + " ",
                               line_width=1.0,
                               alpha=.9,
                               line_color=FFT_COLORS[i])
        self.fft_plot.yaxis.axis_label_text_font_style = 'normal'
        self.exg_plot.line(x='t',
                           y='marker',
                           source=self._marker_source,
                           line_width=1,
                           alpha=.8,
                           line_color='#7AB904',
                           line_dash="4 4")

        for i in range(3):
            self.acc_plot.line(x='t',
                               y=ORN_LIST[i],
                               source=self._orn_source,
                               legend_label=ORN_LIST[i] + " ",
                               line_width=1.5,
                               line_color=LINE_COLORS[i],
                               alpha=.9)
            self.gyro_plot.line(x='t',
                                y=ORN_LIST[i + 3],
                                source=self._orn_source,
                                legend_label=ORN_LIST[i + 3] + " ",
                                line_width=1.5,
                                line_color=LINE_COLORS[i],
                                alpha=.9)
            self.mag_plot.line(x='t',
                               y=ORN_LIST[i + 6],
                               source=self._orn_source,
                               legend_label=ORN_LIST[i + 6] + " ",
                               line_width=1.5,
                               line_color=LINE_COLORS[i],
                               alpha=.9)

        # Set x_range
        self.plot_list = [
            self.exg_plot, self.acc_plot, self.gyro_plot, self.mag_plot
        ]
        self._set_t_range(WIN_LENGTH)

        # Set the formatting of yaxis ticks' labels
        self.exg_plot.yaxis.major_label_overrides = dict(
            zip(range(1, self.n_chan + 1), self.chan_key_list))
        for plot in self.plot_list:
            plot.toolbar.autohide = True
            plot.yaxis.axis_label_text_font_style = 'normal'
            if len(plot.legend) != 0:
                plot.legend.location = "bottom_left"
                plot.legend.orientation = "horizontal"
                plot.legend.padding = 2

    def _init_imp_plot(self):
        plot = figure(plot_width=600,
                      plot_height=200,
                      x_range=self.chan_key_list[0:self.n_chan],
                      y_range=[str(1)],
                      toolbar_location=None)

        plot.circle(x='channel',
                    y="row",
                    radius=.3,
                    source=self.imp_source,
                    fill_alpha=0.6,
                    color="color",
                    line_color='color',
                    line_width=2)

        text_props = {
            "source": self.imp_source,
            "text_align": "center",
            "text_color": "white",
            "text_baseline": "middle",
            "text_font": "helvetica",
            "text_font_style": "bold"
        }

        x = dodge("channel", -0.1, range=plot.x_range)

        plot.text(x=x,
                  y=dodge('row', -.4, range=plot.y_range),
                  text="impedance",
                  **text_props).glyph.text_font_size = "10pt"
        plot.text(x=x,
                  y=dodge('row', -.3, range=plot.y_range),
                  text="channel",
                  **text_props).glyph.text_font_size = "12pt"

        plot.outline_line_color = None
        plot.grid.grid_line_color = None
        plot.axis.axis_line_color = None
        plot.axis.major_tick_line_color = None
        plot.axis.major_label_standoff = 0
        plot.axis.visible = False
        return plot

    def _init_controls(self):
        """Initialize all controls in the dashboard"""
        # EEG/ECG Radio button
        self.mode_control = widgets.Select(title="Signal",
                                           value='EEG',
                                           options=MODE_LIST,
                                           width=210)
        self.mode_control.on_change('value', self._change_mode)

        self.t_range = widgets.Select(title="Time window",
                                      value="10 s",
                                      options=list(TIME_RANGE_MENU.keys()),
                                      width=210)
        self.t_range.on_change('value', self._change_t_range)
        self.y_scale = widgets.Select(title="Y-axis Scale",
                                      value="1 mV",
                                      options=list(SCALE_MENU.keys()),
                                      width=210)
        self.y_scale.on_change('value', self._change_scale)

        # Create device info tables
        columns = [
            widgets.TableColumn(field='heart_rate', title="Heart Rate (bpm)")
        ]
        self.heart_rate = widgets.DataTable(source=self._heart_rate_source,
                                            index_position=None,
                                            sortable=False,
                                            reorderable=False,
                                            columns=columns,
                                            width=210,
                                            height=50)

        columns = [
            widgets.TableColumn(field='firmware_version',
                                title="Firmware Version")
        ]
        self.firmware = widgets.DataTable(source=self._firmware_source,
                                          index_position=None,
                                          sortable=False,
                                          reorderable=False,
                                          columns=columns,
                                          width=210,
                                          height=50)

        columns = [widgets.TableColumn(field='battery', title="Battery (%)")]
        self.battery = widgets.DataTable(source=self._battery_source,
                                         index_position=None,
                                         sortable=False,
                                         reorderable=False,
                                         columns=columns,
                                         width=210,
                                         height=50)

        columns = [
            widgets.TableColumn(field='temperature',
                                title="Device temperature (C)")
        ]
        self.temperature = widgets.DataTable(source=self.temperature_source,
                                             index_position=None,
                                             sortable=False,
                                             reorderable=False,
                                             columns=columns,
                                             width=210,
                                             height=50)

        columns = [widgets.TableColumn(field='light', title="Light (Lux)")]
        self.light = widgets.DataTable(source=self.light_source,
                                       index_position=None,
                                       sortable=False,
                                       reorderable=False,
                                       columns=columns,
                                       width=210,
                                       height=50)

        # Add widgets to the doc
        widget_box = widgetbox([
            Spacer(width=210,
                   height=30), self.mode_control, self.y_scale, self.t_range,
            self.heart_rate, self.battery, self.temperature, self.firmware
        ],
                               width=220)
        return widget_box

    def _init_recorder(self):
        self.rec_button = Toggle(label=u"\u25CF  Record",
                                 button_type="default",
                                 active=False,
                                 width=210)
        self.file_name_widget = TextInput(value="test_file",
                                          title="File name:",
                                          width=210)
        self.file_type_widget = RadioGroup(labels=["EDF (BDF+)", "CSV"],
                                           active=0,
                                           width=210)

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

        self.rec_button.on_click(self._toggle_rec)
        return column(Spacer(width=210, height=5), self.file_name_widget,
                      self.file_type_widget, self.rec_button, self.timer)

    def _toggle_rec(self, active):
        if active:
            if self.explore.is_connected:
                self.explore.record_data(
                    file_name=self.file_name_widget.value,
                    file_type=['edf', 'csv'][self.file_type_widget.active],
                    do_overwrite=True)
                self.rec_button.label = u"\u25A0  Stop"
                self.rec_start_time = datetime.now()
                self.rec_timer_id = self.doc.add_periodic_callback(
                    self._timer_callback, 1000)
            else:
                self.rec_button.active = False
                self.doc.remove_periodic_callback(self.rec_timer_id)
                self.doc.add_next_tick_callback(
                    partial(self._update_rec_timer,
                            new_data={'timer': '00:00:00'}))
        else:
            self.explore.stop_recording()
            self.rec_button.label = u"\u25CF  Record"
            self.doc.add_next_tick_callback(
                partial(self._update_rec_timer, new_data={'timer':
                                                          '00:00:00'}))
            self.doc.remove_periodic_callback(self.rec_timer_id)

    def _timer_callback(self):
        t_delta = (datetime.now() - self.rec_start_time).seconds
        timer_text = ':'.join([
            str(int(t_delta / 3600)).zfill(2),
            str(int(t_delta / 60) % 60).zfill(2),
            str(int(t_delta % 60)).zfill(2)
        ])
        data = {'timer': timer_text}
        self.doc.add_next_tick_callback(
            partial(self._update_rec_timer, new_data=data))

    @gen.coroutine
    @without_property_validation
    def _update_rec_timer(self, new_data):
        self._timer_source.stream(new_data, rollover=1)

    def _set_t_range(self, t_length):
        """Change time range of ExG and orientation plots"""
        for plot in self.plot_list:
            self.win_length = int(t_length)
            plot.x_range.follow = "end"
            plot.x_range.follow_interval = t_length
            plot.x_range.range_padding = 0.
            plot.x_range.min_interval = t_length
Beispiel #28
0
    ticker=CmapAndTicks['ticker'],
    formatter=CmapAndTicks['formatter'],
    label_standoff=2,
    border_line_color=None,
    location=(0, 0),
    bar_line_alpha=0.5,
    major_label_text_align='left',
)
# %% Make Buttons for state graph
"""
# %% Make Buttons for state graph
________________________________________________________________________________
"""
buttons = {}
buttons['state_covid_data'] = Toggle(label="State Time History Graph",
                                     visible=False,
                                     button_type='primary')  #
buttons['state_covid_data'].js_on_change(
    'active',
    CustomJS(args={'p_state_covid': p_state_covid},
             code="""
                  if (cb_obj.active == false){
                      cb_obj.label  = "Show State Time History Graph"
                      p_state_covid.visible = false
                  }
                  else{
                      cb_obj.label  = "Hide State Time History Graph"
                      p_state_covid.visible = true
                      }
                  """))
from bokeh.io import output_file, show
from bokeh.models import Toggle

output_file("toggle.html")

toggle = Toggle(label="Foo", button_type="success")

show(toggle)
Beispiel #30
0
# Import CheckboxGroup, RadioGroup, Toggle from bokeh.models
from bokeh.models import CheckboxGroup, RadioGroup, Toggle
from bokeh.io import curdoc
from bokeh.plotting import figure

from bokeh.layouts import widgetbox

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

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

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

# Add widgetbox(toggle, checkbox, radio) to the current document
curdoc().add_root(widgetbox(toggle, checkbox, radio))
from bokeh.io import output_file, show
from bokeh.plotting import figure
from bokeh.layouts import layout
from bokeh.models import Toggle, BoxAnnotation

output_file("styling_visible_annotation_with_interaction.html")

p = figure(plot_width=600, plot_height=200, tools='')
p.line([1, 2, 3], [1, 2, 1], line_color="blue")
pink_line = p.line([1, 2, 3], [2, 1, 2], line_color="pink")

green_box = BoxAnnotation(left=1.5, right=2.5, fill_color='green', fill_alpha=0.1)
p.add_layout(green_box)

# Use js_link to connect button active property to glyph visble property

toggle1 = Toggle(label="Green Box", button_type="success", active=True)
toggle1.js_link('active', green_box, 'visible')

toggle2 = Toggle(label="Pink Line", button_type="success", active=True)
toggle2.js_link('active', pink_line, 'visible')

show(layout([p], [toggle1, toggle2]))
Beispiel #32
0
                     tools="save,reset",
                     toolbar_location="below")

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

columns = [
    TableColumn(field="x", title="x"),
    TableColumn(field="y", title="y")
]
data_table = DataTable(
    source=source,
    columns=columns,
    index_position=None,
    autosize_mode="fit_columns",
    editable=True,
)

toggle = Toggle()


def cb(attr, old, new):
    columns[0].visible = toggle.active


toggle.on_change('active', cb)

layout = row(plot_figure, data_table, toggle)

curdoc().add_root(layout)
curdoc().title = "Data-Table Bokeh Server"
Beispiel #33
0
other_circles = fig.circle(source=other_source, x="x", y="y", radius="r", fill_color="gray",
            fill_alpha=.3, line_alpha=0, hover_alpha=1, hover_color="yellow", legend="Other")
speed_circles = fig.circle(source=speed_source, x="x", y="y", radius="r", fill_color="blue",
            fill_alpha=.3, line_alpha=0, hover_alpha=1, hover_color="yellow", legend="Speeding")
drunk_circles = fig.circle(source=drunk_source, x="x", y="y", radius="r", fill_color="red",
            fill_alpha=.3, line_alpha=0, hover_alpha=1, hover_color="yellow", legend="Drunk")

dot_tooltips = [("Date", "@MONTH/@DAY/@YEAR"), ("Fatalities", "@FATALS"), ("Drunk", "@DRUNK_DR"),
                ("Speeding", "@SP"), ("Weather", "@WEATHER")]

fig.add_tools(HoverTool(renderers=[other_circles, speed_circles, drunk_circles], tooltips=dot_tooltips))

button_group = CheckboxButtonGroup(
        labels=["Other", "Speeding", "Drunk"], active=[0, 1, 2], width=200)

toggle = Toggle(label="Sort by Hour", button_type="default")

slider = Slider(title="Hour (Military Time)", start=0, end=23, value=0, step=1)

empty_dict = dict(
        x=np.array([]),
        y=np.array([]),
        r=np.array([]),
        MONTH=np.array([]),
        DAY=np.array([]),
        YEAR=np.array([]),
        FATALS=np.array([]),
        DRUNK_DR=np.array([]),
        SP=np.array([]),
        WEATHER=np.array([])
)
# Import CheckboxGroup, RadioGroup, Toggle from bokeh.models
from bokeh.io import curdoc
from bokeh.models import CheckboxGroup, RadioGroup, Toggle
from bokeh.layouts import widgetbox

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

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

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

# Add widgetbox(toggle, checkbox, radio) to the current document
curdoc().add_root(widgetbox(toggle, checkbox, radio))
Beispiel #35
0
def plotDistanceDurationRelation(fileName, dateRange, bokehPlaceholderId='bokehContent'):   
    MAX_AGE = 150
    #The initial data
    data = {
    'x':[], 
    'y':[],
    'ss':[], 
    'ss_lat':[],
    'ss_long':[],
    'es':[], 
    'es_lat':[],
    'es_long':[],
    'distance':[],
    'avg_duration':[],
    'count':[]
    }

    source = ColumnDataSource(data=data)
    ## Input Widgets ##
    textInputTopK = TextInput(value="1000", title="Number of Top Results Selected:")        
    textInputMinDistance = TextInput(value="0.3", title="Minimum Distance Between Stations (km):")    
    textInputMinDuration = TextInput(value="1", title="Minimum Trip Duration (min):")    
    selectUT = Select(title="User Type:", value="All", options=["All", "Subscriber", "Customer"])
    selectGender = Select(title="Gender:", value="All", options=["All", "Male", "Female", "Unknown"])
    sliderAgeGeq = Slider(start=0, end=MAX_AGE, value=0, step=1, title="Age GEQ")
    sliderAgeLeq = Slider(start=0, end=MAX_AGE, value=0, step=1, title="Age LEQ")    
    startDP = DatePicker(title="Start Date:", min_date=dateRange[0] ,max_date=dateRange[1], value=dateRange[0])
    endDP = DatePicker(title="End Date:", min_date=dateRange[0] ,max_date=dateRange[1], value=dateRange[1])
     ## Action Widget ##
    showButton = Toggle(label="Show", type="success")

    ## Output Widget ##    
    columns = [
    TableColumn(field="ss", title="SS"), 
    TableColumn(field="es", title="ES"), 
    TableColumn(field="distance", title="Distance (km)"),
    TableColumn(field="avg_duration", title="Avg Duration (min)"),
    TableColumn(field="count", title="#Trips")
    ]
    data_table = DataTable(source=source, columns=columns, width=850, row_headers=False, selectable='checkbox')
    
    ## Callbacks ##
    model = dict(source=source,textInputTopK=textInputTopK, textInputMinDuration=textInputMinDuration, textInputMinDistance=textInputMinDistance, startDP = startDP, endDP = endDP, selectUT=selectUT,selectGender=selectGender,sliderAgeLeq=sliderAgeLeq, sliderAgeGeq=sliderAgeGeq, dt = data_table)
    
    showCallback = CustomJS(args=model, code="""
        //alert("callback");
        var topK = textInputTopK.get('value');
        var minDuration = textInputMinDuration.get('value');
        var minDistance = textInputMinDistance.get('value');
        var gender = selectGender.get('value');
        var userType = selectUT.get('value'); 
        var startAge = sliderAgeGeq.get('value');
        var endAge = sliderAgeLeq.get('value');
        var startDate = startDP.get('value');
        
        if ( typeof(startDate) !== "number")
            startDate = startDate.getTime();
            
        var endDate = endDP.get('value');
        
        if ( typeof(endDate) !== "number")
            endDate = endDate.getTime();    
        
        var xmlhttp;
        xmlhttp = new XMLHttpRequest();
        
        xmlhttp.onreadystatechange = function() {
            if (xmlhttp.readyState == XMLHttpRequest.DONE ) {
                if(xmlhttp.status == 200){
                    var data = source.get('data');
                    var result = JSON.parse(xmlhttp.responseText);
                    data['ss']=result.ss;
                    data['ss_lat']=result.ss_lat;
                    data['ss_long']=result.ss_long;
                    data['es']=result.es;
                    data['es_lat']=result.es_lat;
                    data['es_long']=result.es_long;
                    data['distance']=result.distance;
                    data['avg_duration']=result.avg_duration;
                    data['count']=result.count;
                    source.trigger('change');
                    dt.trigger('change');
                }
                else if(xmlhttp.status == 400) {
                    alert(400);
                }
                else {
                    alert(xmlhttp.status);
                }
            }
        };
    var params = {topK:topK, minDuration:minDuration, minDistance:minDistance, gender:gender, userType:userType, startAge:startAge, endAge:endAge, startDate:startDate, endDate:endDate};
    url = "/pairsDistanceOverDuration?" + jQuery.param( params );
    xmlhttp.open("GET", url, true);
    xmlhttp.send();
    """)
    
    showButton.callback = showCallback         
    
    TOOLS = "box_zoom,reset,pan,wheel_zoom,save, tap, poly_select"
    plot = Figure(title="Station Pair Distance/Trip Duration Relation", x_axis_label='Distance Between Stations (km)', y_axis_label='Average Trip Duration (min)', plot_width=850, plot_height=400, tools=TOOLS)
    plot.circle('distance', 'avg_duration', source=source, color="navy", size = 5, alpha = 0.7)
    
    l1 = vform(plot, data_table)
    tab1 = Panel(child = vform(textInputTopK, textInputMinDistance), title="Station Pairs Filters")  
    tab2 = Panel(child = vform(textInputMinDuration,  selectUT, selectGender,sliderAgeLeq, sliderAgeGeq, startDP, endDP), title="Trips Filters")
    tabs = Tabs(tabs=[tab1, tab2])
    layout = hplot(vplot(tabs, showButton), l1)
    script, div = components(layout)
    html = readHtmlFile(fileName)
    html = insertScriptIntoHeader(html, script)
    html = appendElementContent(html, div, "div", "bokehContent")

    return html 
Beispiel #36
0
bt = Button(label='Update Plot')
bt.on_click(my_slider_handler)


def toggle_small_handler(attr, old, new):
    print(toggle_small.active)
    my_slider_handler()


def toggle_hexes_handler(attr, old, new):
    print(toggle_hexes.active)
    my_slider_handler()


toggle_small = Toggle(label="Toggle Points", button_type="success")
toggle_small.on_change("active", toggle_small_handler)

toggle_hexes = Toggle(label="Toggle Hexes", button_type="success")
toggle_hexes.on_change("active", toggle_hexes_handler)

CID = list(set(df['customer_id'].astype('str')))
print('CID', len(CID))
select_cid = Select(title="Select Customer IDs:", value="foo", options=CID)

from bokeh.tile_providers import CARTODBPOSITRON

map_repr = 'mercator'
# set up/draw the map
p = figure(
    #    x_range=(minlng,maxlng),
Beispiel #37
0
def create():
    det_data = {}
    roi_selection = {}

    upload_div = Div(text="Open .cami file:")

    def upload_button_callback(_attr, _old, new):
        with io.StringIO(base64.b64decode(new).decode()) as file:
            h5meta_list = pyzebra.parse_h5meta(file)
            file_list = h5meta_list["filelist"]
            filelist.options = file_list
            filelist.value = file_list[0]

    upload_button = FileInput(accept=".cami")
    upload_button.on_change("value", upload_button_callback)

    def update_image(index=None):
        if index is None:
            index = index_spinner.value

        current_image = det_data["data"][index]
        proj_v_line_source.data.update(x=np.arange(0, IMAGE_W) + 0.5,
                                       y=np.mean(current_image, axis=0))
        proj_h_line_source.data.update(x=np.mean(current_image, axis=1),
                                       y=np.arange(0, IMAGE_H) + 0.5)

        image_source.data.update(
            h=[np.zeros((1, 1))],
            k=[np.zeros((1, 1))],
            l=[np.zeros((1, 1))],
        )
        image_source.data.update(image=[current_image])

        if auto_toggle.active:
            im_max = int(np.max(current_image))
            im_min = int(np.min(current_image))

            display_min_spinner.value = im_min
            display_max_spinner.value = im_max

            image_glyph.color_mapper.low = im_min
            image_glyph.color_mapper.high = im_max

    def update_overview_plot():
        h5_data = det_data["data"]
        n_im, n_y, n_x = h5_data.shape
        overview_x = np.mean(h5_data, axis=1)
        overview_y = np.mean(h5_data, axis=2)

        overview_plot_x_image_source.data.update(image=[overview_x], dw=[n_x])
        overview_plot_y_image_source.data.update(image=[overview_y], dw=[n_y])

        if frame_button_group.active == 0:  # Frame
            overview_plot_x.axis[1].axis_label = "Frame"
            overview_plot_y.axis[1].axis_label = "Frame"

            overview_plot_x_image_source.data.update(y=[0], dh=[n_im])
            overview_plot_y_image_source.data.update(y=[0], dh=[n_im])

        elif frame_button_group.active == 1:  # Omega
            overview_plot_x.axis[1].axis_label = "Omega"
            overview_plot_y.axis[1].axis_label = "Omega"

            om = det_data["rot_angle"]
            om_start = om[0]
            om_end = (om[-1] - om[0]) * n_im / (n_im - 1)
            overview_plot_x_image_source.data.update(y=[om_start], dh=[om_end])
            overview_plot_y_image_source.data.update(y=[om_start], dh=[om_end])

    def filelist_callback(_attr, _old, new):
        nonlocal det_data
        det_data = pyzebra.read_detector_data(new)

        index_spinner.value = 0
        index_spinner.high = det_data["data"].shape[0] - 1
        update_image(0)
        update_overview_plot()

    filelist = Select()
    filelist.on_change("value", filelist_callback)

    def index_spinner_callback(_attr, _old, new):
        update_image(new)

    index_spinner = Spinner(title="Image index:", value=0, low=0)
    index_spinner.on_change("value", index_spinner_callback)

    plot = Plot(
        x_range=Range1d(0, IMAGE_W, bounds=(0, IMAGE_W)),
        y_range=Range1d(0, IMAGE_H, bounds=(0, IMAGE_H)),
        plot_height=IMAGE_H * 3,
        plot_width=IMAGE_W * 3,
        toolbar_location="left",
    )

    # ---- tools
    plot.toolbar.logo = None

    # ---- axes
    plot.add_layout(LinearAxis(), place="above")
    plot.add_layout(LinearAxis(major_label_orientation="vertical"),
                    place="right")

    # ---- grid lines
    plot.add_layout(Grid(dimension=0, ticker=BasicTicker()))
    plot.add_layout(Grid(dimension=1, ticker=BasicTicker()))

    # ---- rgba image glyph
    image_source = ColumnDataSource(
        dict(
            image=[np.zeros((IMAGE_H, IMAGE_W), dtype="float32")],
            h=[np.zeros((1, 1))],
            k=[np.zeros((1, 1))],
            l=[np.zeros((1, 1))],
            x=[0],
            y=[0],
            dw=[IMAGE_W],
            dh=[IMAGE_H],
        ))

    h_glyph = Image(image="h", x="x", y="y", dw="dw", dh="dh", global_alpha=0)
    k_glyph = Image(image="k", x="x", y="y", dw="dw", dh="dh", global_alpha=0)
    l_glyph = Image(image="l", x="x", y="y", dw="dw", dh="dh", global_alpha=0)

    plot.add_glyph(image_source, h_glyph)
    plot.add_glyph(image_source, k_glyph)
    plot.add_glyph(image_source, l_glyph)

    image_glyph = Image(image="image", x="x", y="y", dw="dw", dh="dh")
    plot.add_glyph(image_source, image_glyph, name="image_glyph")

    # ---- projections
    proj_v = Plot(
        x_range=plot.x_range,
        y_range=DataRange1d(),
        plot_height=200,
        plot_width=IMAGE_W * 3,
        toolbar_location=None,
    )

    proj_v.add_layout(LinearAxis(major_label_orientation="vertical"),
                      place="right")
    proj_v.add_layout(LinearAxis(major_label_text_font_size="0pt"),
                      place="below")

    proj_v.add_layout(Grid(dimension=0, ticker=BasicTicker()))
    proj_v.add_layout(Grid(dimension=1, ticker=BasicTicker()))

    proj_v_line_source = ColumnDataSource(dict(x=[], y=[]))
    proj_v.add_glyph(proj_v_line_source,
                     Line(x="x", y="y", line_color="steelblue"))

    proj_h = Plot(
        x_range=DataRange1d(),
        y_range=plot.y_range,
        plot_height=IMAGE_H * 3,
        plot_width=200,
        toolbar_location=None,
    )

    proj_h.add_layout(LinearAxis(), place="above")
    proj_h.add_layout(LinearAxis(major_label_text_font_size="0pt"),
                      place="left")

    proj_h.add_layout(Grid(dimension=0, ticker=BasicTicker()))
    proj_h.add_layout(Grid(dimension=1, ticker=BasicTicker()))

    proj_h_line_source = ColumnDataSource(dict(x=[], y=[]))
    proj_h.add_glyph(proj_h_line_source,
                     Line(x="x", y="y", line_color="steelblue"))

    # add tools
    hovertool = HoverTool(tooltips=[("intensity",
                                     "@image"), ("h", "@h"), ("k",
                                                              "@k"), ("l",
                                                                      "@l")])

    box_edit_source = ColumnDataSource(dict(x=[], y=[], width=[], height=[]))
    box_edit_glyph = Rect(x="x",
                          y="y",
                          width="width",
                          height="height",
                          fill_alpha=0,
                          line_color="red")
    box_edit_renderer = plot.add_glyph(box_edit_source, box_edit_glyph)
    boxedittool = BoxEditTool(renderers=[box_edit_renderer], num_objects=1)

    def box_edit_callback(_attr, _old, new):
        if new["x"]:
            h5_data = det_data["data"]
            x_val = np.arange(h5_data.shape[0])
            left = int(np.floor(new["x"][0]))
            right = int(np.ceil(new["x"][0] + new["width"][0]))
            bottom = int(np.floor(new["y"][0]))
            top = int(np.ceil(new["y"][0] + new["height"][0]))
            y_val = np.sum(h5_data[:, bottom:top, left:right], axis=(1, 2))
        else:
            x_val = []
            y_val = []

        roi_avg_plot_line_source.data.update(x=x_val, y=y_val)

    box_edit_source.on_change("data", box_edit_callback)

    wheelzoomtool = WheelZoomTool(maintain_focus=False)
    plot.add_tools(
        PanTool(),
        BoxZoomTool(),
        wheelzoomtool,
        ResetTool(),
        hovertool,
        boxedittool,
    )
    plot.toolbar.active_scroll = wheelzoomtool

    # shared frame range
    frame_range = DataRange1d()
    det_x_range = DataRange1d()
    overview_plot_x = Plot(
        title=Title(text="Projections on X-axis"),
        x_range=det_x_range,
        y_range=frame_range,
        plot_height=400,
        plot_width=400,
        toolbar_location="left",
    )

    # ---- tools
    wheelzoomtool = WheelZoomTool(maintain_focus=False)
    overview_plot_x.toolbar.logo = None
    overview_plot_x.add_tools(
        PanTool(),
        BoxZoomTool(),
        wheelzoomtool,
        ResetTool(),
    )
    overview_plot_x.toolbar.active_scroll = wheelzoomtool

    # ---- axes
    overview_plot_x.add_layout(LinearAxis(axis_label="Coordinate X, pix"),
                               place="below")
    overview_plot_x.add_layout(LinearAxis(axis_label="Frame",
                                          major_label_orientation="vertical"),
                               place="left")

    # ---- grid lines
    overview_plot_x.add_layout(Grid(dimension=0, ticker=BasicTicker()))
    overview_plot_x.add_layout(Grid(dimension=1, ticker=BasicTicker()))

    # ---- rgba image glyph
    overview_plot_x_image_source = ColumnDataSource(
        dict(image=[np.zeros((1, 1), dtype="float32")],
             x=[0],
             y=[0],
             dw=[1],
             dh=[1]))

    overview_plot_x_image_glyph = Image(image="image",
                                        x="x",
                                        y="y",
                                        dw="dw",
                                        dh="dh")
    overview_plot_x.add_glyph(overview_plot_x_image_source,
                              overview_plot_x_image_glyph,
                              name="image_glyph")

    det_y_range = DataRange1d()
    overview_plot_y = Plot(
        title=Title(text="Projections on Y-axis"),
        x_range=det_y_range,
        y_range=frame_range,
        plot_height=400,
        plot_width=400,
        toolbar_location="left",
    )

    # ---- tools
    wheelzoomtool = WheelZoomTool(maintain_focus=False)
    overview_plot_y.toolbar.logo = None
    overview_plot_y.add_tools(
        PanTool(),
        BoxZoomTool(),
        wheelzoomtool,
        ResetTool(),
    )
    overview_plot_y.toolbar.active_scroll = wheelzoomtool

    # ---- axes
    overview_plot_y.add_layout(LinearAxis(axis_label="Coordinate Y, pix"),
                               place="below")
    overview_plot_y.add_layout(LinearAxis(axis_label="Frame",
                                          major_label_orientation="vertical"),
                               place="left")

    # ---- grid lines
    overview_plot_y.add_layout(Grid(dimension=0, ticker=BasicTicker()))
    overview_plot_y.add_layout(Grid(dimension=1, ticker=BasicTicker()))

    # ---- rgba image glyph
    overview_plot_y_image_source = ColumnDataSource(
        dict(image=[np.zeros((1, 1), dtype="float32")],
             x=[0],
             y=[0],
             dw=[1],
             dh=[1]))

    overview_plot_y_image_glyph = Image(image="image",
                                        x="x",
                                        y="y",
                                        dw="dw",
                                        dh="dh")
    overview_plot_y.add_glyph(overview_plot_y_image_source,
                              overview_plot_y_image_glyph,
                              name="image_glyph")

    def frame_button_group_callback(_active):
        update_overview_plot()

    frame_button_group = RadioButtonGroup(labels=["Frames", "Omega"], active=0)
    frame_button_group.on_click(frame_button_group_callback)

    roi_avg_plot = Plot(
        x_range=DataRange1d(),
        y_range=DataRange1d(),
        plot_height=IMAGE_H * 3,
        plot_width=IMAGE_W * 3,
        toolbar_location="left",
    )

    # ---- tools
    roi_avg_plot.toolbar.logo = None

    # ---- axes
    roi_avg_plot.add_layout(LinearAxis(), place="below")
    roi_avg_plot.add_layout(LinearAxis(major_label_orientation="vertical"),
                            place="left")

    # ---- grid lines
    roi_avg_plot.add_layout(Grid(dimension=0, ticker=BasicTicker()))
    roi_avg_plot.add_layout(Grid(dimension=1, ticker=BasicTicker()))

    roi_avg_plot_line_source = ColumnDataSource(dict(x=[], y=[]))
    roi_avg_plot.add_glyph(roi_avg_plot_line_source,
                           Line(x="x", y="y", line_color="steelblue"))

    cmap_dict = {
        "gray": Greys256,
        "gray_reversed": Greys256[::-1],
        "plasma": Plasma256,
        "cividis": Cividis256,
    }

    def colormap_callback(_attr, _old, new):
        image_glyph.color_mapper = LinearColorMapper(palette=cmap_dict[new])
        overview_plot_x_image_glyph.color_mapper = LinearColorMapper(
            palette=cmap_dict[new])
        overview_plot_y_image_glyph.color_mapper = LinearColorMapper(
            palette=cmap_dict[new])

    colormap = Select(title="Colormap:", options=list(cmap_dict.keys()))
    colormap.on_change("value", colormap_callback)
    colormap.value = "plasma"

    radio_button_group = RadioButtonGroup(labels=["nb", "nb_bi"], active=0)

    STEP = 1

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

        update_image()

    auto_toggle = Toggle(label="Auto Range",
                         active=True,
                         button_type="default")
    auto_toggle.on_click(auto_toggle_callback)

    # ---- colormap display max value
    def display_max_spinner_callback(_attr, _old_value, new_value):
        display_min_spinner.high = new_value - STEP
        image_glyph.color_mapper.high = new_value

    display_max_spinner = Spinner(
        title="Maximal Display Value:",
        low=0 + STEP,
        value=1,
        step=STEP,
        disabled=auto_toggle.active,
    )
    display_max_spinner.on_change("value", display_max_spinner_callback)

    # ---- colormap display min value
    def display_min_spinner_callback(_attr, _old_value, new_value):
        display_max_spinner.low = new_value + STEP
        image_glyph.color_mapper.low = new_value

    display_min_spinner = Spinner(
        title="Minimal Display Value:",
        high=1 - STEP,
        value=0,
        step=STEP,
        disabled=auto_toggle.active,
    )
    display_min_spinner.on_change("value", display_min_spinner_callback)

    def hkl_button_callback():
        index = index_spinner.value
        setup_type = "nb_bi" if radio_button_group.active else "nb"
        h, k, l = calculate_hkl(det_data, index, setup_type)
        image_source.data.update(h=[h], k=[k], l=[l])

    hkl_button = Button(label="Calculate hkl (slow)")
    hkl_button.on_click(hkl_button_callback)

    selection_list = TextAreaInput(rows=7)

    def selection_button_callback():
        nonlocal roi_selection
        selection = [
            int(np.floor(det_x_range.start)),
            int(np.ceil(det_x_range.end)),
            int(np.floor(det_y_range.start)),
            int(np.ceil(det_y_range.end)),
            int(np.floor(frame_range.start)),
            int(np.ceil(frame_range.end)),
        ]

        filename_id = filelist.value[-8:-4]
        if filename_id in roi_selection:
            roi_selection[f"{filename_id}"].append(selection)
        else:
            roi_selection[f"{filename_id}"] = [selection]

        selection_list.value = str(roi_selection)

    selection_button = Button(label="Add selection")
    selection_button.on_click(selection_button_callback)

    # Final layout
    layout_image = column(
        gridplot([[proj_v, None], [plot, proj_h]], merge_tools=False),
        row(index_spinner))
    colormap_layout = column(colormap, auto_toggle, display_max_spinner,
                             display_min_spinner)
    hkl_layout = column(radio_button_group, hkl_button)

    layout_overview = column(
        gridplot(
            [[overview_plot_x, overview_plot_y]],
            toolbar_options=dict(logo=None),
            merge_tools=True,
        ),
        frame_button_group,
    )

    tab_layout = row(
        column(
            upload_div,
            upload_button,
            filelist,
            layout_image,
            row(colormap_layout, hkl_layout),
        ),
        column(
            roi_avg_plot,
            layout_overview,
            row(selection_button, selection_list),
        ),
    )

    return Panel(child=tab_layout, title="Data Viewer")
Beispiel #38
0
counter = 0
def run(new):
    global p, patches, colors, counter

    for _ in range(slider.value):
        counter += 1
        data = patches.data_source.data.copy()
        rates = np.random.uniform(0, 100, size=100).tolist()
        color = [colors[2 + int(rate / 16.667)] for rate in rates]

        p.title = 'Algorithms Deployed, Iteration: {}'.format(counter)
        source.data['rate'] = rates
        source.data['color'] = color
        time.sleep(5)

toggle = Toggle(label='START')
toggle.on_click(run)

slider = Slider(name='N iterations to advance',
                title='N iterations to advance',
                start=5,
                end=10000,
                step=5,
                value=500)

# set up layout
toggler = HBox(toggle)
inputs = VBox(toggler, slider)

# add to document
curdoc().add_root(HBox(inputs))
from bokeh.io import show
from bokeh.models import CustomJS, Toggle

toggle = Toggle(label="Foo", button_type="success")
toggle.js_on_event(
    'button_click',
    CustomJS(args=dict(btn=toggle),
             code="""
    console.log('toggle: active=' + btn.active, this.toString())
"""))

show(toggle)
Beispiel #40
0
    TableColumn(field="S", title="Salinity"),
    TableColumn(field="N", title="Nitrate"),
    TableColumn(field="O", title="Oxygen"),
    TableColumn(field="Z", title="Tidal Height")
]
data_table = DataTable(source=source, columns=columns,
                       width=300, height=600)


def toggle_callback(attr):
    if tide_toggle.active:
        # Checked *after* press
        tide_toggle.label = "Disable Tides"
    else:
        tide_toggle.label = "Enable Tides"
tide_toggle = Toggle(label="Enable Tides", callback=toggle_ocean)
tide_toggle.on_click(toggle_callback)

download_button = Button(label="Download data", callback=download_data)

go_button = Button(label="Run model")#, callback=check_fish)
go_button.on_click(update_plots)


# Set up app layout
prods = VBox(gas_exchange_slider, productivity_slider)
river = VBox(river_flow_slider, river_N_slider)
tide_run = HBox(tide_toggle, download_button, go_button)
all_settings = VBox(prods, river, tide_run,
                    width=400)
Beispiel #41
0
                          StringEditor, StringFormatter, Switch, TableColumn,
                          Tabs, TextAreaInput, TextInput, Toggle)
from bokeh.plotting import figure
from bokeh.resources import INLINE
from bokeh.sampledata.autompg2 import autompg2 as mpg
from bokeh.sampledata.iris import flowers
from bokeh.util.browser import view

click_button = Button(label="Button still has click event",
                      button_type="success")

disabled_button = Button(label="Button (disabled) - still has click event",
                         button_type="primary",
                         disabled=True)

toggle = Toggle(label="Toggle button", button_type="success")

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

dropdown = Dropdown(label="Dropdown button", button_type="warning", menu=menu)
dropdown_split = Dropdown(label="Split button",
                          button_type="danger",
                          menu=menu,
                          split=True)

checkbox_group = CheckboxGroup(labels=["Option 1", "Option 2", "Option 3"],
                               active=[0, 1])
radio_group = RadioGroup(labels=["Option 1", "Option 2", "Option 3"], active=0)

checkbox_button_group = CheckboxButtonGroup(