Пример #1
0
    result = most_common
    source.trigger('change')


#dropdown zipcode input
zip_menu = [("94102", "94102"), ("94103", "94103"), ("94104", "94104"),
            ("94105", "94105"), ("94107", "94107"), ("94108", "94108"),
            ("94109", "94109"), ("94110", "94110"), ("94111", "94111"),
            ("94112", "94112"), ("94114", "94114"), ("94115", "94115"),
            ("94116", "94116"), ("94117", "94117"), ("94118", "94118"),
            ("94121", "94121"), ("94122", "94122"), ("94123", "94123"),
            ("94124", "94124"), ("94127", "94127"), ("94129", "94129"),
            ("94130", "94130"), ("94131", "94131"), ("94132", "94132"),
            ("94133", "94133"), ("94134", "94134"), ("94158", "94158")]
zipdrop = Dropdown(label="Zipcode",
                   button_type="warning",
                   menu=zip_menu,
                   callback=CustomJS.from_py_func(callback))
z = zipdrop.value

#dropdown time input
menu = [("12:00 AM", "0"), ("1:00 AM", "1"),
        ("2:00 AM", "2"), ("3:00 AM", "3"), ("4:00 AM", "4"), ("5:00 AM", "5"),
        ("6:00 AM", "6"), ("7:00 AM", "7"), ("8:00 AM", "8"), ("9:00 AM", "9"),
        ("10:00 AM", "10"), ("11:00 AM", "11"), ("12:00 PM", "12"),
        ("1:00 PM", "13"), ("2:00 PM", "14"), ("3:00 PM", "15"),
        ("4:00 PM", "16"), ("5:00 PM", "17"), ("6:00 PM", "18"),
        ("7:00 PM", "19"), ("8:00 PM", "20"), ("9:00 PM", "21"),
        ("10:00 PM", "22"), ("11:00 PM", "23")]
timedrop = Dropdown(label="Time of Day",
                    button_type="warning",
                    menu=menu,
Пример #2
0
    #StringFormatter, NumberFormatter,
    #StringEditor, IntEditor, NumberEditor, SelectEditor,
)

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

layout = column(
    Button(label="Default Button 1", button_type="default"),
    Button(label="Primary Button 2", button_type="primary"),
    Button(label="Success Button 3", button_type="success"),

    Toggle(label="Default Toggle 1", button_type="default"),
    Toggle(label="Primary Toggle 2", button_type="primary"),
    Toggle(label="Success Toggle 3", button_type="success"),

    Dropdown(label="Default Dropdown 1", button_type="default", menu=menu),
    Dropdown(label="Primary Dropdown 2", button_type="primary", menu=menu),
    Dropdown(label="Success Dropdown 3", button_type="success", menu=menu),

    CheckboxButtonGroup(labels=["Checkbox Option 1", "Checkbox Option 2", "Checkbox Option 3"], button_type="default", active=[0, 1]),
    CheckboxButtonGroup(labels=["Checkbox Option 4", "Checkbox Option 5", "Checkbox Option 6"], button_type="primary", active=[1, 2]),
    CheckboxButtonGroup(labels=["Checkbox Option 7", "Checkbox Option 8", "Checkbox Option 9"], button_type="success", active=[0, 2]),

    RadioButtonGroup(labels=["Radio Option 1", "Radio Option 2", "Radio Option 3"], button_type="default", active=0),
    RadioButtonGroup(labels=["Radio Option 4", "Radio Option 5", "Radio Option 6"], button_type="primary", active=1),
    RadioButtonGroup(labels=["Radio Option 7", "Radio Option 8", "Radio Option 9"], button_type="success", active=2),

    TextInput(placeholder="TextInput 1"),
    TextInput(placeholder="TextInput 2"),
    TextInput(placeholder="TextInput 3"),
Пример #3
0
def make_dropdown(prop, menu):
    dropdown = Dropdown(label=prop, menu=menu)
    add_callback(dropdown, prop)
    return dropdown
Пример #4
0
                  alpha=0.6,
                  line_color="black",
                  line_width=2)
ds3 = p3.data_source

p4 = plot4.circle(x='xdata4',
                  y='ydata4',
                  source=source,
                  size='size4',
                  color='ColorList4',
                  alpha=0.6,
                  line_color="black",
                  line_width=2)
ds4 = p4.data_source

dropdownX1 = Dropdown(label="X value1", button_type="default", menu=menu)
dropdownX2 = Dropdown(label="X value2", button_type="default", menu=menu)
dropdownX3 = Dropdown(label="X value3", button_type="default", menu=menu)
dropdownX4 = Dropdown(label="X value4", button_type="default", menu=menu)


def dropdownX_handler1(new):
    dictionary['xdata1'] = dictionary[new.item]
    ds1.data = dictionary
    plot1.xaxis.axis_label = new.item


def dropdownX_handler2(new):
    dictionary['xdata2'] = dictionary[new.item]
    ds2.data = dictionary
    plot2.xaxis.axis_label = new.item
Пример #5
0
x = [3, 4, 6, 12, 10, 1]
y = [7, 1, 3, 4, 1, 6]
c = ['blue', 'blue', 'blue', 'blue', 'blue', 'blue']
source = ColumnDataSource(data=dict(x=x, y=y, color=c))

plot_figure = figure(title='Dropdown',
                     plot_height=450,
                     plot_width=600,
                     tools="save,reset",
                     toolbar_location="below")

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

menu = [("Red", "red"), ("Green", "green")]
dropdown = Dropdown(label="Choose color for plot", menu=menu)


def dropdown_click(attr, old, new):
    active_dropdown = dropdown.value  ##Getting dropdown value

    if active_dropdown == 'red':
        c = ['red', 'red', 'red', 'red', 'red', 'red']
    elif active_dropdown == 'green':
        c = ['green', 'green', 'green', 'green', 'green', 'green']
    source.data = dict(x=x, y=y, color=c)


dropdown.on_change('value', dropdown_click)

layout = row(dropdown, plot_figure)
Пример #6
0
from bokeh.sampledata.iris import flowers
from bokeh.util.browser import view

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

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

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

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

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

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

checkbox_button_group = CheckboxButtonGroup(
    labels=["Option 1", "Option 2", "Option 3"], active=[0, 1])
radio_button_group = RadioButtonGroup(
    labels=["Option 1", "Option 2", "Option 3"], active=0)

checkbox_button_group_vertical = CheckboxButtonGroup(
    title='Select Y Axis',
    options=sorted(head_columns.keys()),
    value='Amplifier words'
)

sel_xaxis = Select(
    title='Select X Axis',
    options=sorted(head_columns.keys()),
    value='Outcome'
)

options=['scatter', 'vbar', 'varea', 'line']

sel_plot = Dropdown(
    label='Select plot type',
    button_type='success',
    menu=options,
    value='line'
)

add_row = FileInput(
)

inp_id = TextInput(
    placeholder='Insert ID',
    value='1',
)

sh_ess = TextAreaInput(
    title='Essay %s: 338 words' % inp_id.value,
    rows=30,
    cols=50,
Пример #8
0
                  start=0,
                  end=3,
                  step=0.02)
r0_slider = Slider(title="Basic reproduction number",
                   value=r0,
                   start=0,
                   end=3,
                   step=0.02)
Nt_slider = Slider(title="Total time (days)",
                   value=Nt,
                   start=10,
                   end=max_Nt,
                   step=1)
country_opts = country_list
dropdown = Dropdown(labe="Select Country",
                    button_type="warning",
                    menu=country_opts)


def update_data(attrname, old, new):
    # Get the current slider values
    a = N0_slider.value
    b = r_slider.value
    c = dt
    d = Nt_slider.value
    e = r0_slider.value
    f = dropdown.value

    D = np.zeros(d + 2)
    D[0] = a
def categoricalPlot(dataset_id, columnName):
    log = DatasetManager.query.get_or_404(dataset_id)
    datasetName = log.datasetName
    datasetSqlName = log.datasetSqlName
    df = pd.read_sql_table(datasetSqlName, db.engine)
    vc = df[columnName].value_counts()
    categories = vc.index.to_list()

    # Categorical values can also be used as coordinates
    values = vc.to_list()
    print("categories and values:", categories, values)

    source = ColumnDataSource(data=dict(categories=categories, values=values))
    print("source dir=", dir(source))
    print("source.data=", source.data)
    print("source.data=", source.data["categories"])

    # Set the x_range to the list of categories above
    p = figure(
        x_range=source.data["categories"],
        plot_height=250,
        plot_width=1100,
        title=datasetName + ": " + columnName + " Counts",
    )

    p.vbar(
        x="categories",
        top="values",
        width=0.9,
        source=source,
        legend_field="categories",
        line_color="white",
        fill_color=factor_cmap("categories",
                               palette=Spectral6,
                               factors=source.data["categories"]),
    )

    # Set some properties to make the plot look better
    p.xgrid.grid_line_color = None
    p.y_range.start = 0
    # p.y_range.end = 9
    p.legend.orientation = "horizontal"
    p.legend.location = "top_center"

    # Create menu and options with name of data columns with numerical data
    columns = df.columns.to_list()
    menu = []
    columnNameList = []
    category_counts = []
    for columnName in columns:
        if not (is_numeric_dtype(df[columnName])):
            # Menu is a list of tuples of name/value pairs for dropdown widget
            menu.append((columnName, columnName))
            # Option is a list of column names for multi-select widget
            columnNameList.append(columnName)
            # Get value counts
            vc = df[columnName].value_counts()
            category_counts.append([vc.index.to_list(), vc.to_list()])
    # print("category_counts =", category_counts)

    # Create dropdown widget
    dropdown = Dropdown(label="Select Column",
                        button_type="primary",
                        menu=menu,
                        width_policy="min")
    category_counts_source = ColumnDataSource(
        data=dict(x=columnNameList, y=category_counts))
    # Define JS callback to change column to plot
    print("p.x_range=", p.x_range.properties_with_values())
    print(dir(p.x_range))
    callback_dropdown = CustomJS(
        args=dict(source=source,
                  category_counts=category_counts_source,
                  x_range=p.x_range),
        code="""
        console.log('dropdown=' + this.item);
        // Initialize x and y arrays
        var data = source.data;
        var categories = data['categories'];
        // console.log(' x=' + x);
        var values = data['values'];
        // console.log('y=' + y);
        console.log('categories=' + categories + ' values=' + values);

        // Get dataframe
        var category_counts_data = category_counts.data;
        console.log('category_counts_data=' + category_counts_data);
        console.dir(category_counts_data);
        console.log('category_counts_data.x=' + category_counts_data.x);
        console.dir(category_counts_data.x);
        console.dir(category_counts_data.y);
        var index = category_counts_data.x.indexOf(this.item);
        console.log('index=' + index);
        console.log(category_counts_data.y[index]);
        // categories = category_counts_data.y[index][0];
        // values = category_counts_data.y[index][1];

        source.data['categories'] = category_counts_data.y[index][0];
        source.data['values'] = category_counts_data.y[index][1];
        x_range.factors = source.data['categories'];

        console.log('categories=' + categories + ' values=' + values);
        // Update plot with new x,y source data
        source.change.emit();
        """,
    )

    # Define javascript events to trigger callbacks
    dropdown.js_on_event(
        "menu_item_click",
        callback_dropdown,
    )
    # return row(dropdown, p)
    return p
Пример #10
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)
Пример #11
0
        bar_chart2.visible = False
        bar_chart3.visible = False
    elif new == '2e Klass':
        bar_chart.visible = False
        bar_chart1.visible = False
        bar_chart2.visible = True
        bar_chart3.visible = False
    elif new == '3e Klass':
        bar_chart.visible = False
        bar_chart1.visible = False
        bar_chart2.visible = False
        bar_chart3.visible = True


# Create a dropdown Select widget: select
selectRegio = Dropdown(label="Maak keuze uit de klasse",
                       menu=["All", "1e Klass", "2e Klass", "3e Klass"])

# Attach the update_plot callback to the 'value' property of select
selectRegio.on_click(update_bar_chart)

# Hoofdstuk 3 Scatter en 2-D visualisatie
titel4 = Div(text="<h2"
             ">Hoofdstuk 3: Scatterplots en 2-D visualisaties"
             "</h2>",
             width=800,
             height=50)
text4 = Div(
    text="<h4"
    ">Hieronder volgt scatterplot van prijs per ticket tegenover de GDP per capita"
    "</h4>",
    width=800,
Пример #12
0
    def test_server_on_change_round_trip(
            self, bokeh_server_page: BokehServerPage) -> None:
        button = Dropdown(label="Dropdown button", menu=items)

        def modify_doc(doc):
            source = ColumnDataSource(dict(x=[1, 2], y=[1, 1]))
            plot = Plot(height=400,
                        width=400,
                        x_range=Range1d(0, 1),
                        y_range=Range1d(0, 1),
                        min_border=0)
            plot.add_glyph(source, Circle(x='x', y='y', size=20))
            plot.tags.append(
                CustomJS(name="custom-action",
                         args=dict(s=source),
                         code=RECORD("data", "s.data")))

            def cb(event):
                item = event.item
                if item == "item_1_value":
                    source.data = dict(x=[10, 20], y=[10, 10])
                elif item == "item_2_value":
                    source.data = dict(x=[100, 200], y=[100, 100])
                elif item == "item_3_value":
                    source.data = dict(x=[1000, 2000], y=[1000, 1000])

            button.on_event('menu_item_click', cb)
            doc.add_root(column(button, plot))

        page = bokeh_server_page(modify_doc)

        button_el = find_element_for(page.driver, button, "button")
        button_el.click()

        item = find_element_for(page.driver, button,
                                ".bk-menu > *:nth-child(1)")
        item.click()

        page.eval_custom_action()

        results = page.results
        assert results == {'data': {'x': [10, 20], 'y': [10, 10]}}

        button_el = find_element_for(page.driver, button, "button")
        button_el.click()

        item = find_element_for(page.driver, button,
                                ".bk-menu > *:nth-child(3)")
        item.click()

        page.eval_custom_action()

        results = page.results
        assert results == {'data': {'x': [1000, 2000], 'y': [1000, 1000]}}

        button_el = find_element_for(page.driver, button, "button")
        button_el.click()

        item = find_element_for(page.driver, button,
                                ".bk-menu > *:nth-child(2)")
        item.click()

        page.eval_custom_action()

        results = page.results
        assert results == {'data': {'x': [100, 200], 'y': [100, 100]}}
Пример #13
0
           fill_color=factor_cmap('x',
                                  palette=palette,
                                  factors=result['genders'],
                                  start=1,
                                  end=2))

# create a plot and style its properties
p.y_range.start = 0
p.x_range.range_padding = 0.025
p.xaxis.major_label_orientation = 1
p.xgrid.grid_line_color = None


# create a callback
def dropdown_callback(attr, old, new):
    result = getMedals(new)
    counts = sum(zip(result['data']['men'], result['data']['women']), ())
    p.x_range = FactorRange(*result['range'])
    r.data_source.data = dict(x=result['range'], counts=counts)


# button details, range restrictions for display, with the callback
menu = [("1900", "1900"), ("1920", "1920"), ("1950", "1950"), ("1960", "1960"),
        ("1970", "1970"), ("1980", "1980"), ("1990", "1990"), ("2000", "2000"),
        ("2010", "2010"), ("2016", "2016")]
dropdown = Dropdown(label="Start Year", button_type="warning", menu=menu)
dropdown.on_change("value", dropdown_callback)

# add button to the graph page
curdoc().add_root(column(dropdown, p))
Пример #14
0
    },
    'eta_modifier': {
        'SGD': 50,
        'AdaGrad': 50,
        'RMSProp': 1,
        'Momemtum': 50,
        'Nesterov': 50,
        'Adam': 5,
        'Nadam': 5,
        'Activate': 50
    }
}

dropdowns = {
    'activation': Dropdown(label='Sigmoid',
                           menu=menus['activation'],
                           width=245),
    'optimizer': Dropdown(label='SGD', menu=menus['optimizer'], width=245)
}

for name, dropdown in dropdowns.items():
    dropdown.on_click(
        partial(callback_dropdown, ref=dropdown, selector=selectors[name]))

configs = {'num_tiles': 500}

weight_0, weight_1 = np.meshgrid(
    np.linspace(-20., 20., num=configs['num_tiles']),
    np.linspace(-20., 20., num=configs['num_tiles']))

tf.random.set_seed(0)
Пример #15
0
    def _setup_widgets(self):
        """
        Create annotator widgets and assign Python callbacks.
        """
        from bokeh.models import TextInput, Button, Dropdown

        super()._setup_widgets()

        self.annotator_input = TextInput(title="Label:")
        self.annotator_apply = Button(
            label="Apply",
            button_type="primary",
            height_policy="fit",
            width_policy="min",
        )
        self.annotator_export = Dropdown(
            label="Export",
            button_type="warning",
            menu=["Excel", "CSV", "JSON", "pickle"],
            height_policy="fit",
            width_policy="min",
        )

        def callback_apply():
            """
            A callback on clicking the 'self.annotator_apply' button.

            Update labels in the source.
            """
            label = self.annotator_input.value
            selected_idx = self.sources["raw"].selected.indices
            if not selected_idx:
                self._warn(
                    "Attempting annotation: did not select any data points. Eligible subset is 'raw'."
                )
                return
            example_old = self.dfs["raw"].at[selected_idx[0], "label"]
            self.dfs["raw"].at[selected_idx, "label"] = label
            example_new = self.dfs["raw"].at[selected_idx[0], "label"]
            self._good(
                f"Applied {len(selected_idx)} annotations: {label} (e.g. {example_old} -> {example_new}"
            )

            self._update_sources()
            self.plot()
            self._good(f"Updated annotator plot at {current_time()}")

        def callback_export(event, path_root=None):
            """
            A callback on clicking the 'self.annotator_export' button.

            Saves the dataframe to a pickle.
            """
            export_format = event.item

            # auto-determine the export path root
            if path_root is None:
                timestamp = current_time("%Y%m%d%H%M%S")
                path_root = f"hover-annotated-df-{timestamp}"

            export_df = pd.concat(self.dfs,
                                  axis=0,
                                  sort=False,
                                  ignore_index=True)

            if export_format == "Excel":
                export_path = f"{path_root}.xlsx"
                export_df.to_excel(export_path, index=False)
            elif export_format == "CSV":
                export_path = f"{path_root}.csv"
                export_df.to_csv(export_path, index=False)
            elif export_format == "JSON":
                export_path = f"{path_root}.json"
                export_df.to_json(export_path, orient="records")
            elif export_format == "pickle":
                export_path = f"{path_root}.pkl"
                export_df.to_pickle(export_path)
            else:
                raise ValueError(f"Unexpected export format {export_format}")

            self._good(f"Saved DataFrame to {export_path}")

        # keep the references to the callbacks
        self._callback_apply = callback_apply
        self._callback_export = callback_export

        # assign callbacks
        self.annotator_apply.on_click(self._callback_apply)
        self.annotator_export.on_click(self._callback_export)
def multilinePlot(dataset_id, columnName):
    log = DatasetManager.query.get_or_404(dataset_id)
    datasetName = log.datasetName
    datasetSqlName = log.datasetSqlName
    df = pd.read_sql_table(datasetSqlName, db.engine)
    # prepare some data
    x = df["index"]
    # create a new plot with a title and axis labels
    p = figure(
        title="Dataset Name: " + datasetName,
        x_axis_label="Index",
        y_axis_label="y",
        sizing_mode="fixed",
        plot_width=1100,
        plot_height=400,
    )

    # Initialize source to x=[x] and y=[x]
    # Note:
    # Multi-line plots take an array as x's and y's
    # The x and y arrays must be the same length
    initialColumnName = columnName
    y = df[columnName]
    source = ColumnDataSource(data=dict(x=[x], y=[y]))
    df_source = ColumnDataSource(data=dict(df))
    p.multi_line("x", "y", source=source)

    # Create menu and options with name of data columns with numerical data
    columns = df.columns.to_list()
    menu = []
    options = []
    for columnName in columns:
        # if df[columnName].dtype == np.float64 or df[columnName].dtype == np.int64:
        if is_numeric_dtype(df[columnName]):
            # Menu is a list of tuples of name/value pairs for dropdown widget
            menu.append((columnName, columnName))
            # Option is a list of column names for multi-select widget
            options.append(columnName)

    # Create dropdown widget
    dropdown = Dropdown(label="Select Column",
                        button_type="primary",
                        menu=menu,
                        width_policy="min")
    # Create multi-select widget
    multi_choice = MultiChoice(value=[initialColumnName],
                               options=options,
                               title="Select Columns")

    # Define JS callback to change column to plot
    callback_dropdown = CustomJS(
        args=dict(source=source, df_source=df_source),
        code="""
        // Initialize x and y arrays
        var data = source.data;
        var x = data['x'];
        // console.log(' x=' + x);
        var y = data['y'];
        // console.log('y=' + y);

        // Get dataframe
        var df_data = df_source.data;

        // Set y array to selected value
        y[0] = df_data[this.item];

        // Update plot with new x,y source data
        source.change.emit();
        """,
    )
    callback_multi_select = CustomJS(
        args=dict(source=source, df_source=df_source),
        code="""
        // Initialize x and y arrays
        var data = source.data;
        var x = data['x'];
        var y = data['y'];
        x.length = 0;
        y.length = 0;
        // console.log(' x=' + x);
        // console.log('x.length=' + x.length);
        // console.log('y=' + y);
        // console.log('y.length=' + y.length);

        // Get dataframe
        var df_data = df_source.data;

        // Get value of multi-select widget
        // console.log('multi_select: ' + this.value, this.value.toString());
        var array = this.value;
        

        // Iterate through multi-select values and update x,y arrays with selected values
        var array_iterator = array.values();
        let next_value = array_iterator.next();
        while (!next_value.done) {
        // console.log(next_value.value);
        x.push(df_data['index']);
        y.push(df_data[next_value.value]);
        next_value = array_iterator.next();
        }
        // console.log('y=' + y);
        // console.log('y.length=' + y.length);
        // console.log('x.length=' + x.length);

        // Update plot with new x,y source data
        source.change.emit();
        """,
    )

    # Define javascript events to trigger callbacks
    dropdown.js_on_event(
        "menu_item_click",
        callback_dropdown,
    )

    multi_choice.js_on_change(
        "value",
        callback_multi_select,
    )

    layout = column(multi_choice, p)
    return layout
from bokeh.io import show
from bokeh.models import CustomJS, Dropdown

menu = [("Item 1", "item_1"), ("Item 2", "item_2"), None, ("Item 3", "item_3")]

dropdown = Dropdown(label="Dropdown button", button_type="warning", menu=menu)
dropdown.js_on_event(
    "menu_item_click",
    CustomJS(code="console.log('dropdown: ' + this.item, this.toString())"))

show(dropdown)
Пример #18
0
)
alarm.visible = False
alarm_list = Div(
    text=
    '<div class="container"><h3>Dispositivos que presentan problemas:</h3><ul></ul></div>'
)
alarm_list.visible = False

dataRecordingButton = Button(label="Comenzar a adquirir datos",
                             button_type="success")
dataRecordingLabel = Div(text='<p>Adquiriendo datos...</p>')
dataRecordingLabel.visible = False

extensionsMenu = [("csv", "csv"), ("txt", "txt"), ("npy", "npy")]
extensionsDropdown = Dropdown(label="Guardar los datos con extensión:",
                              button_type="success",
                              menu=extensionsMenu)
''' ******************** Modo Automático ******************** '''

label1 = Div(text='<h1>Modo Automático &#9889;</h1><hr>')
refEst1 = Slider(title="Altura de Referencia Estanque 1",
                 value=ref1,
                 start=0.0,
                 end=50.0,
                 step=0.1)
refEst2 = Slider(title="Altura de Referencia Estanque 2",
                 value=ref2,
                 start=0.0,
                 end=50.0,
                 step=0.1)
Пример #19
0
def visualize_clusters(clustering, filter_list: Optional[list] = None,
                       texts: Optional[list] = None,
                       radius: float = 0.05,
                       alpha: float = 0.5,
                       plot_width: int = 1000, plot_height: int = 530,
                       output_in_notebook: bool = True,
                       output_file_path: Optional[str] = None,
                       palette: Union[list, str] = 'Wellcome33'):
    """
    This function creates a plot of the clusters

    Args:
        clustering: wellcomeml.ml.TextClustering instance
        filter_list: list
        texts: A list of texts to be displayed by the hover function
        radius: float, default: 0.05
        alpha: float, default: 0.5
        plot_width: int, default: 600
        plot_height: int, default: 600
        output_in_notebook: bool, default: True
        output_file_path: str, default: 'cluster_viz.html'
        palette: list, default: Wellcome33

    Returns:
        None (Prints a bokeh figure)
    """

    # Dataframe creation
    reduced_points = clustering.reduced_points
    data = pd.DataFrame(reduced_points)
    data = data.rename(columns={0: 'X', 1: 'Y'})
    data['cluster_id'] = clustering.cluster_ids
    data['cluster_id'] = data['cluster_id'].astype(str)
    data['Keywords'] = clustering.cluster_kws
    data['category'] = (filter_list if filter_list else ['All']*len(data))

    # Palette setting
    palette = (Wellcome33 if palette == 'Wellcome33' else palette)
    palette = [str(x) for x in palette]
    well_background = str(WellcomeBackground)
    clusters = list(data['cluster_id'])
    clusters = list(map(int, clusters))
    clusters_uniq = np.unique(clusters)
    data['colors'] = [(palette[x % len(palette)]
                       if x != -1 else str(WellcomeNoData)) for x in clusters]

    tools = ('hover, pan, wheel_zoom, zoom_in, zoom_out, reset, save, tap')
    tooltips = [("index", "$index"), ("(x,y)", "($x, $y)"),
                ("cluster", "@cluster_id"), ("keywords", "@Keywords")]

    if texts is not None:
        # Only gets the 60 characters of the text
        data['text'] = [text[:60] + '...' for text in texts]
        tooltips += [("text", "@text")]

    # DropDown Button
    dropdown_options = list(set([('All', 'All'), None] +
                                [(cat, cat) for i, cat in enumerate(sorted(
                                    data['category'].unique()), 2)]))
    dropdown = Dropdown(label='Category', button_type='default',
                        menu=dropdown_options, width=190, align="end")

    # Defines figure for plotting the clusters
    p = figure(title="Cluster visualization", toolbar_location="above",
               plot_width=plot_width, plot_height=plot_height,
               tools=tools, tooltips=tooltips,
               background_fill_color=well_background)

    R = []
    sources = []
    filtered_sources = []
    for x in clusters_uniq:
        data_cluster_id_unfiltered = data[data['cluster_id'] == str(x)]

        sources.append(ColumnDataSource(data_cluster_id_unfiltered))
        filtered_sources.append(ColumnDataSource(data_cluster_id_unfiltered))

        # Plots the cluster
        r = p.circle(x="X", y="Y", radius=radius, fill_alpha=alpha,
                     color="colors", source=filtered_sources[-1])

        R += [r]

    # JavaScript callback for the Dropdown Button
    callback = CustomJS(
        args=dict(sources=sources, filtered_sources=filtered_sources),
        code="""

    var data = []
    var cat = cb_obj.item;

    function generateNewDataObject(oldDataObject){
        var newDataObject = {}
        for (var key of Object.keys(oldDataObject)){
            newDataObject[key] = [];
        }
        return newDataObject
    }

    function addRowToAccumulator(accumulator, dataObject, index) {
        for (var key of Object.keys(dataObject)){
            accumulator[key][index] = dataObject[key][index];
        }
        return accumulator;
    }

    if (cat === 'All') {
        for (var i = 0; i < sources.length; i++) {
            data.push(sources[i].data);
        }
    } else {
        for (var i = 0; i < sources.length; i++) {
            let new_data =  generateNewDataObject(sources[i].data);
            for (var j = 0; j <= sources[i].data['category'].length; j++) {
                if (sources[i].data['category'][j] == cat) {
                    new_data = addRowToAccumulator(new_data, sources[i].data,
                                                   j);
                }
            }
            data[i] = new_data
        }
    }
     for (var i = 0; i < sources.length; i++) {
        filtered_sources[i].data = data[i]
        filtered_sources[i].change.emit()
    }
    """
    )
    dropdown.js_on_event(MenuItemClick, callback)

    # Plots the legend on two columns
    if len(clusters_uniq) > 36:
        median = len(R) // 2
        legend1 = Legend(items=[(str(s), [r]) for s, r in
                                zip(clusters_uniq[:median], R[:median])])
        legend2 = Legend(items=[(str(s), [r]) for s, r in
                                zip(clusters_uniq[median:], R[median:])])
        p.add_layout(legend1, 'right')
        p.add_layout(legend2, 'right')
    else:
        legend = Legend(items=[(str(s), [r]) for s, r in
                               zip(clusters_uniq, R)])
        p.add_layout(legend, 'right')

    # Plots other extra annotations to the plot
    p.legend.title = "Cluster ID"
    p.legend.label_text_font_size = "11px"
    p.legend.background_fill_color = str(WellcomeBackground)
    p.legend.click_policy = "hide"
    p.min_border_left = 200

    # Output in notebook and new page
    reset_output()
    if output_in_notebook:
        output_notebook()

    if output_file_path:
        output_file(output_file_path)

    show(column(dropdown, p))
Пример #20
0
dwell_unloop_point_view = CDSView(source=dwell_unloop_point,
                                  filters=[dwell_unloop_filter])

loop_point_view = CDSView(source=loop_point, filters=[loop_filter])
loop_endog_view = CDSView(source=loop_endog, filters=[endog_filter])

post_endog_view = CDSView(source=post_endog, filters=[endog_filter])
post_point_view = CDSView(source=post_point)

# Define the dropdown for the interactivity
menu_dict = {m: m for m in mut_df['mutant'].unique() if m != 'V10-95'}
menu_dict['DFL161'] = "DFL16.1-5"
menu_dict['DFL1613'] = "DFL16.1-3"
menu = [(v, k) for k, v in menu_dict.items()
        if k in ['V1-135', 'V8-18', 'V5-43']]
sel = Dropdown(value='', label='Select Sequence Combination', menu=menu)

# Define the figure canvas

# Define the figure canvases
seq_ax = bokeh.plotting.figure(width=600,
                               height=50,
                               x_range=[0, 30],
                               y_range=[-0.01, 0.1],
                               tools=[''],
                               toolbar_location=None)
dwell_all_ax = bokeh.plotting.figure(
    width=275,
    height=250,
    x_axis_type='log',
    x_range=[0.5, 80],
Пример #21
0
def create(palm):
    fit_max = 1
    fit_min = 0

    doc = curdoc()

    # THz calibration plot
    scan_plot = Plot(
        title=Title(text="THz calibration"),
        x_range=DataRange1d(),
        y_range=DataRange1d(),
        plot_height=PLOT_CANVAS_HEIGHT,
        plot_width=PLOT_CANVAS_WIDTH,
        toolbar_location="right",
    )

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

    # ---- axes
    scan_plot.add_layout(LinearAxis(axis_label="Stage delay motor"),
                         place="below")
    scan_plot.add_layout(LinearAxis(axis_label="Energy shift, eV",
                                    major_label_orientation="vertical"),
                         place="left")

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

    # ---- circle cluster glyphs
    scan_circle_source = ColumnDataSource(dict(x=[], y=[]))
    scan_plot.add_glyph(scan_circle_source,
                        Circle(x="x", y="y", line_alpha=0, fill_alpha=0.5))

    # ---- circle glyphs
    scan_avg_circle_source = ColumnDataSource(dict(x=[], y=[]))
    scan_plot.add_glyph(
        scan_avg_circle_source,
        Circle(x="x", y="y", line_color="purple", fill_color="purple"))

    # ---- line glyphs
    fit_line_source = ColumnDataSource(dict(x=[], y=[]))
    scan_plot.add_glyph(fit_line_source, Line(x="x",
                                              y="y",
                                              line_color="purple"))

    # THz calibration folder path text input
    def path_textinput_callback(_attr, _old_value, _new_value):
        update_load_dropdown_menu()
        path_periodic_update()

    path_textinput = TextInput(title="THz calibration path:",
                               value=os.path.join(os.path.expanduser("~")),
                               width=510)
    path_textinput.on_change("value", path_textinput_callback)

    # THz calibration eco scans dropdown
    def scans_dropdown_callback(_attr, _old_value, new_value):
        scans_dropdown.label = new_value

    scans_dropdown = Dropdown(label="ECO scans",
                              button_type="default",
                              menu=[])
    scans_dropdown.on_change("value", scans_dropdown_callback)

    # ---- eco scans periodic update
    def path_periodic_update():
        new_menu = []
        if os.path.isdir(path_textinput.value):
            for entry in os.scandir(path_textinput.value):
                if entry.is_file() and entry.name.endswith(".json"):
                    new_menu.append((entry.name, entry.name))
        scans_dropdown.menu = sorted(new_menu, reverse=True)

    doc.add_periodic_callback(path_periodic_update, 5000)

    # Calibrate button
    def calibrate_button_callback():
        palm.calibrate_thz(
            path=os.path.join(path_textinput.value, scans_dropdown.value))
        fit_max_spinner.value = np.ceil(palm.thz_calib_data.index.values.max())
        fit_min_spinner.value = np.floor(
            palm.thz_calib_data.index.values.min())
        update_calibration_plot()

    def update_calibration_plot():
        scan_plot.xaxis.axis_label = f"{palm.thz_motor_name}, {palm.thz_motor_unit}"

        scan_circle_source.data.update(
            x=np.repeat(palm.thz_calib_data.index,
                        palm.thz_calib_data["peak_shift"].apply(len)).tolist(),
            y=np.concatenate(
                palm.thz_calib_data["peak_shift"].values).tolist(),
        )

        scan_avg_circle_source.data.update(
            x=palm.thz_calib_data.index.tolist(),
            y=palm.thz_calib_data["peak_shift_mean"].tolist())

        x = np.linspace(fit_min, fit_max, 100)
        y = palm.thz_slope * x + palm.thz_intersect
        fit_line_source.data.update(x=np.round(x, decimals=5),
                                    y=np.round(y, decimals=5))

        calib_const_div.text = f"""
        thz_slope = {palm.thz_slope}
        """

    calibrate_button = Button(label="Calibrate THz",
                              button_type="default",
                              width=250)
    calibrate_button.on_click(calibrate_button_callback)

    # THz fit maximal value text input
    def fit_max_spinner_callback(_attr, old_value, new_value):
        nonlocal fit_max
        if new_value > fit_min:
            fit_max = new_value
            palm.calibrate_thz(
                path=os.path.join(path_textinput.value, scans_dropdown.value),
                fit_range=(fit_min, fit_max),
            )
            update_calibration_plot()
        else:
            fit_max_spinner.value = old_value

    fit_max_spinner = Spinner(title="Maximal fit value:",
                              value=fit_max,
                              step=0.1)
    fit_max_spinner.on_change("value", fit_max_spinner_callback)

    # THz fit maximal value text input
    def fit_min_spinner_callback(_attr, old_value, new_value):
        nonlocal fit_min
        if new_value < fit_max:
            fit_min = new_value
            palm.calibrate_thz(
                path=os.path.join(path_textinput.value, scans_dropdown.value),
                fit_range=(fit_min, fit_max),
            )
            update_calibration_plot()
        else:
            fit_min_spinner.value = old_value

    fit_min_spinner = Spinner(title="Minimal fit value:",
                              value=fit_min,
                              step=0.1)
    fit_min_spinner.on_change("value", fit_min_spinner_callback)

    # Save calibration button
    def save_button_callback():
        palm.save_thz_calib(path=path_textinput.value)
        update_load_dropdown_menu()

    save_button = Button(label="Save", button_type="default", width=250)
    save_button.on_click(save_button_callback)

    # Load calibration button
    def load_dropdown_callback(_attr, _old_value, new_value):
        palm.load_thz_calib(os.path.join(path_textinput.value, new_value))
        update_calibration_plot()

    def update_load_dropdown_menu():
        new_menu = []
        calib_file_ext = ".palm_thz"
        if os.path.isdir(path_textinput.value):
            for entry in os.scandir(path_textinput.value):
                if entry.is_file() and entry.name.endswith((calib_file_ext)):
                    new_menu.append(
                        (entry.name[:-len(calib_file_ext)], entry.name))
            load_dropdown.button_type = "default"
            load_dropdown.menu = sorted(new_menu, reverse=True)
        else:
            load_dropdown.button_type = "danger"
            load_dropdown.menu = new_menu

    doc.add_next_tick_callback(update_load_dropdown_menu)
    doc.add_periodic_callback(update_load_dropdown_menu, 5000)

    load_dropdown = Dropdown(label="Load", menu=[], width=250)
    load_dropdown.on_change("value", load_dropdown_callback)

    # Calibration constants
    calib_const_div = Div(text=f"""
        thz_slope = {0}
        """)

    # assemble
    tab_layout = column(
        row(
            scan_plot,
            Spacer(width=30),
            column(
                path_textinput,
                scans_dropdown,
                calibrate_button,
                fit_max_spinner,
                fit_min_spinner,
                row(save_button, load_dropdown),
                calib_const_div,
            ),
        ))

    return Panel(child=tab_layout, title="THz Calibration")
Пример #22
0
def geoplot(gdf_in,
            figure=None,
            figsize=None,
            title="",
            xlabel="Longitude",
            ylabel="Latitude",
            xlim=None,
            ylim=None,
            color="blue",
            colormap=None,
            colormap_uselog=False,
            colormap_range=None,
            category=None,
            dropdown=None,
            slider=None,
            slider_range=None,
            slider_name="",
            show_colorbar=True,
            colorbar_tick_format=None,
            xrange=None,
            yrange=None,
            hovertool=True,
            hovertool_columns=[],
            hovertool_string=None,
            simplify_shapes=None,
            tile_provider="CARTODBPOSITRON_RETINA",
            tile_provider_url=None,
            tile_attribution="",
            tile_alpha=1,
            panning=True,
            zooming=True,
            toolbar_location="right",
            show_figure=True,
            return_figure=True,
            return_html=False,
            legend=True,
            webgl=True,
            **kwargs):
    """Doc-String: TODO"""

    # Imports:
    import bokeh.plotting
    from bokeh.plotting import show
    from bokeh.models import (
        HoverTool,
        LogColorMapper,
        LinearColorMapper,
        GeoJSONDataSource,
        WheelZoomTool,
        ColorBar,
        BasicTicker,
        LogTicker,
        Dropdown,
        Slider,
        ColumnDataSource,
    )
    from bokeh.models.callbacks import CustomJS
    from bokeh.models.widgets import Dropdown
    from bokeh.palettes import all_palettes
    from bokeh.layouts import row, column

    # Make a copy of the input geodataframe:
    gdf = gdf_in.copy()

    # Check layertypes:
    if type(gdf) != pd.DataFrame:
        layertypes = []
        if "Point" in str(gdf.geom_type.unique()):
            layertypes.append("Point")
        if "Line" in str(gdf.geom_type.unique()):
            layertypes.append("Line")
        if "Polygon" in str(gdf.geom_type.unique()):
            layertypes.append("Polygon")
        if len(layertypes) > 1:
            raise Exception(
                "Can only plot GeoDataFrames/Series with single type of geometry (either Point, Line or Polygon). Provided is a GeoDataFrame/Series with types: %s"
                % layertypes)
    else:
        layertypes = ["Point"]

    # Get and check provided parameters for geoplot:
    figure_options = {
        "title": title,
        "x_axis_label": xlabel,
        "y_axis_label": ylabel,
        "plot_width": 600,
        "plot_height": 400,
        "toolbar_location": toolbar_location,
        "active_scroll": "wheel_zoom",
        "x_axis_type": "mercator",
        "y_axis_type": "mercator",
    }
    if not figsize is None:
        width, height = figsize
        figure_options["plot_width"] = width
        figure_options["plot_height"] = height
    if webgl:
        figure_options["output_backend"] = "webgl"

    if type(gdf) != pd.DataFrame:
        # Convert GeoDataFrame to Web Mercator Projection:
        gdf = gdf.to_crs({"init": "epsg:3857"})

        # Simplify shapes if wanted:
        if isinstance(simplify_shapes, numbers.Number):
            if layertypes[0] in ["Line", "Polygon"]:
                gdf["geometry"] = gdf["geometry"].simplify(simplify_shapes)
        elif not simplify_shapes is None:
            raise ValueError(
                "<simplify_shapes> parameter only accepts numbers or None.")

    # Check for category, dropdown or slider (choropleth map column):
    category_options = 0
    if not category is None:
        category_options += 1
        category_columns = [category]
    if not dropdown is None:
        category_options += 1
        category_columns = dropdown
    if not slider is None:
        category_options += 1
        category_columns = slider
    if category_options > 1:
        raise ValueError(
            "Only one of <category>, <dropdown> or <slider> parameters is allowed to be used at once."
        )

    # Check for category (single choropleth plot):
    if category is None:
        pass
    elif isinstance(category, (list, tuple)):
        raise ValueError(
            "For <category>, please provide an existing single column of the GeoDataFrame."
        )
    elif category in gdf.columns:
        pass
    else:
        raise ValueError(
            "Could not find column '%s' in GeoDataFrame. For <category>, please provide an existing single column of the GeoDataFrame."
            % category)

    # Check for dropdown (multiple choropleth plots via dropdown selection):
    if dropdown is None:
        pass
    elif not isinstance(dropdown, (list, tuple)):
        raise ValueError(
            "For <dropdown>, please provide a list/tuple of existing columns of the GeoDataFrame."
        )
    else:
        for col in dropdown:
            if col not in gdf.columns:
                raise ValueError(
                    "Could not find column '%s' for <dropdown> in GeoDataFrame. "
                    % col)

    # Check for slider (multiple choropleth plots via slider selection):
    if slider is None:
        pass
    elif not isinstance(slider, (list, tuple)):
        raise ValueError(
            "For <slider>, please provide a list/tuple of existing columns of the GeoDataFrame."
        )
    else:
        for col in slider:
            if col not in gdf.columns:
                raise ValueError(
                    "Could not find column '%s' for <slider> in GeoDataFrame. "
                    % col)

        if not slider_range is None:
            if not isinstance(slider_range, Iterable):
                raise ValueError(
                    "<slider_range> has to be a type that is iterable like list, tuple, range, ..."
                )
            else:
                slider_range = list(slider_range)
                if len(slider_range) != len(slider):
                    raise ValueError(
                        "The number of elements in <slider_range> has to be the same as in <slider>."
                    )
                steps = []
                for i in range(len(slider_range) - 1):
                    steps.append(slider_range[i + 1] - slider_range[i])

                if len(set(steps)) > 1:
                    raise ValueError(
                        "<slider_range> has to have equal step size between each elements (like a range-object)."
                    )
                else:
                    slider_step = steps[0]
                    slider_start = slider_range[0]
                    slider_end = slider_range[-1]

    # Check colormap if either <category>, <dropdown> or <slider> is choosen:
    if category_options == 1:
        if colormap is None:
            colormap = blue_colormap
        elif isinstance(colormap, (tuple, list)):
            if len(colormap) > 1:
                pass
            else:
                raise ValueError(
                    "<colormap> only accepts a list/tuple of at least two colors or the name of one of the following predefined colormaps (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s"
                    % (list(all_palettes.keys())))
        elif isinstance(colormap, str):
            if colormap in all_palettes:
                colormap = all_palettes[colormap]
                colormap = colormap[max(colormap.keys())]
            else:
                raise ValueError(
                    "Could not find <colormap> with name %s. The following predefined colormaps are supported (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s"
                    % (colormap, list(all_palettes.keys())))
        else:
            raise ValueError(
                "<colormap> only accepts a list/tuple of at least two colors or the name of one of the following predefined colormaps (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s"
                % (list(all_palettes.keys())))
    else:
        if isinstance(color, str):
            colormap = [color]
        elif color is None:
            colormap = ["blue"]
        else:
            raise ValueError(
                "<color> has to be a string specifying the fill_color of the map glyph."
            )

    # Check xlim & ylim:
    if xlim is not None:
        if isinstance(xlim, (tuple, list)):
            if len(xlim) == 2:
                from pyproj import Proj, transform

                inProj = Proj(init="epsg:4326")
                outProj = Proj(init="epsg:3857")
                xmin, xmax = xlim
                for _ in [xmin, xmax]:
                    if not -180 < _ <= 180:
                        raise ValueError(
                            "Limits for x-axis (=Longitude) have to be between -180 and 180."
                        )
                if not xmin < xmax:
                    raise ValueError("xmin has to be smaller than xmax.")
                xmin = transform(inProj, outProj, xmin, 0)[0]
                xmax = transform(inProj, outProj, xmax, 0)[0]
                figure_options["x_range"] = (xmin, xmax)
            else:
                raise ValueError(
                    "Limits for x-axis (=Longitude) have to be of form [xmin, xmax] with values between -180 and 180."
                )
        else:
            raise ValueError(
                "Limits for x-axis (=Longitude) have to be of form [xmin, xmax] with values between -180 and 180."
            )
    if ylim is not None:
        if isinstance(ylim, (tuple, list)):
            if len(ylim) == 2:
                from pyproj import Proj, transform

                inProj = Proj(init="epsg:4326")
                outProj = Proj(init="epsg:3857")
                ymin, ymax = ylim
                for _ in [ymin, ymax]:
                    if not -90 < _ <= 90:
                        raise ValueError(
                            "Limits for y-axis (=Latitude) have to be between -90 and 90."
                        )
                if not ymin < ymax:
                    raise ValueError("ymin has to be smaller than ymax.")
                ymin = transform(inProj, outProj, 0, ymin)[1]
                ymax = transform(inProj, outProj, 0, ymax)[1]
                figure_options["y_range"] = (ymin, ymax)
            else:
                raise ValueError(
                    "Limits for y-axis (=Latitude) have to be of form [ymin, ymax] with values between -90 and 90."
                )
        else:
            raise ValueError(
                "Limits for y-axis (=Latitude) have to be of form [ymin, ymax] with values between -90 and 90."
            )

    # Create Figure to draw:
    old_layout = None
    if figure is None:
        p = bokeh.plotting.figure(**figure_options)

        # Add Tile Source as Background:
        p = _add_backgroundtile(p, tile_provider, tile_provider_url,
                                tile_attribution, tile_alpha)

    elif isinstance(figure, type(bokeh.plotting.figure())):
        p = figure
    elif isinstance(figure, type(column())):
        old_layout = figure
        p = _get_figure(old_layout)
    else:
        raise ValueError(
            "Parameter <figure> has to be of type bokeh.plotting.figure or bokeh.layouts.column."
        )

    # Get ridd of zoom on axes:
    for t in p.tools:
        if type(t) == WheelZoomTool:
            t.zoom_on_axis = False

    # Hide legend if wanted:
    legend_input = legend
    if isinstance(legend, str):
        pass
    else:
        legend = "GeoLayer"

    # Define colormapper:
    if len(colormap) == 1:
        kwargs["fill_color"] = colormap[0]

    elif not category is None:
        # Check if category column is numerical:
        if not issubclass(gdf[category].dtype.type, np.number):
            raise NotImplementedError(
                "<category> plot only yet implemented for numerical columns. Column '%s' is not numerical."
                % category)

        field = category
        colormapper_options = {"palette": colormap}
        if not colormap_range is None:
            if not isinstance(colormap_range, (tuple, list)):
                raise ValueError(
                    "<colormap_range> can only be 'None' or a tuple/list of form (min, max)."
                )
            elif len(colormap_range) == 2:
                colormapper_options["low"] = colormap_range[0]
                colormapper_options["high"] = colormap_range[1]
        else:
            colormapper_options["low"] = gdf[field].min()
            colormapper_options["high"] = gdf[field].max()
        if colormap_uselog:
            colormapper = LogColorMapper(**colormapper_options)
        else:
            colormapper = LinearColorMapper(**colormapper_options)
        kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper}
        if not isinstance(legend, str):
            legend = str(field)

    elif not dropdown is None:
        # Check if all columns in dropdown selection are numerical:
        for col in dropdown:
            if not issubclass(gdf[col].dtype.type, np.number):
                raise NotImplementedError(
                    "<dropdown> plot only yet implemented for numerical columns. Column '%s' is not numerical."
                    % col)

        field = dropdown[0]
        colormapper_options = {"palette": colormap}
        if not colormap_range is None:
            if not isinstance(colormap_range, (tuple, list)):
                raise ValueError(
                    "<colormap_range> can only be 'None' or a tuple/list of form (min, max)."
                )
            elif len(colormap_range) == 2:
                colormapper_options["low"] = colormap_range[0]
                colormapper_options["high"] = colormap_range[1]
        else:
            colormapper_options["low"] = gdf[dropdown].min().min()
            colormapper_options["high"] = gdf[dropdown].max().max()
        if colormap_uselog:
            colormapper = LogColorMapper(**colormapper_options)
        else:
            colormapper = LinearColorMapper(**colormapper_options)
        kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper}
        legend = " " + field

    elif not slider is None:
        # Check if all columns in dropdown selection are numerical:
        for col in slider:
            if not issubclass(gdf[col].dtype.type, np.number):
                raise NotImplementedError(
                    "<slider> plot only yet implemented for numerical columns. Column '%s' is not numerical."
                    % col)

        field = slider[0]
        colormapper_options = {"palette": colormap}
        if not colormap_range is None:
            if not isinstance(colormap_range, (tuple, list)):
                raise ValueError(
                    "<colormap_range> can only be 'None' or a tuple/list of form (min, max)."
                )
            elif len(colormap_range) == 2:
                colormapper_options["low"] = colormap_range[0]
                colormapper_options["high"] = colormap_range[1]
        else:
            colormapper_options["low"] = gdf[slider].min().min()
            colormapper_options["high"] = gdf[slider].max().max()
        if colormap_uselog:
            colormapper = LogColorMapper(**colormapper_options)
        else:
            colormapper = LinearColorMapper(**colormapper_options)
        kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper}
        if not isinstance(legend, str):
            legend = "Geolayer"

    # Check that only hovertool_columns or hovertool_string is used:
    if isinstance(hovertool_columns, (list, tuple, str)):
        if len(hovertool_columns) > 0 and hovertool_string is not None:
            raise ValueError(
                "Either <hovertool_columns> or <hovertool_string> can be used, but not both at the same time."
            )
    else:
        raise ValueError(
            "<hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'."
        )

    if hovertool_string is not None:
        hovertool_columns = "all"

    # Check for Hovertool columns:
    if hovertool:
        if not isinstance(hovertool_columns, (list, tuple)):
            if hovertool_columns == "all":
                hovertool_columns = list(
                    filter(lambda col: col != "geometry", gdf.columns))
            else:
                raise ValueError(
                    "<hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'."
                )
        elif len(hovertool_columns) == 0:
            if not category is None:
                hovertool_columns = [category]
            elif not dropdown is None:
                hovertool_columns = dropdown
            elif not slider is None:
                hovertool_columns = slider
            else:
                hovertool_columns = []
        else:
            for col in hovertool_columns:
                if col not in gdf.columns:
                    raise ValueError(
                        "Could not find columns '%s' in GeoDataFrame. <hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'."
                        % col)
    else:
        if category is None:
            hovertool_columns = []
        else:
            hovertool_columns = [category]

    # Reduce DataFrame to needed columns:
    if type(gdf) == pd.DataFrame:
        gdf["Geometry"] = 0
        additional_columns = ["x", "y"]
    else:
        additional_columns = ["geometry"]
    for kwarg, value in kwargs.items():
        if isinstance(value, Hashable):
            if value in gdf.columns:
                additional_columns.append(value)
    if category_options == 0:
        gdf = gdf[list(set(hovertool_columns) | set(additional_columns))]
    else:
        gdf = gdf[list(
            set(hovertool_columns) | set(category_columns)
            | set(additional_columns))]
        gdf["Colormap"] = gdf[field]
        field = "Colormap"

    # Create GeoJSON DataSource for Plot:
    if type(gdf) != pd.DataFrame:
        geo_source = GeoJSONDataSource(geojson=gdf.to_json())
    else:
        geo_source = gdf

    # Draw Glyph on Figure:
    layout = None
    if "Point" in layertypes:
        if "line_color" not in kwargs:
            kwargs["line_color"] = kwargs["fill_color"]
        glyph = p.scatter(x="x",
                          y="y",
                          source=geo_source,
                          legend=legend,
                          **kwargs)

    if "Line" in layertypes:
        if "line_color" not in kwargs:
            kwargs["line_color"] = kwargs["fill_color"]
            del kwargs["fill_color"]
        glyph = p.multi_line(xs="xs",
                             ys="ys",
                             source=geo_source,
                             legend=legend,
                             **kwargs)

    if "Polygon" in layertypes:

        if "line_color" not in kwargs:
            kwargs["line_color"] = "black"

        # Creates from a geoDataFrame with Polygons and Multipolygons a Pandas DataFrame
        # with x any y columns specifying the geometry of the Polygons:
        geo_source = ColumnDataSource(convert_geoDataFrame_to_patches(gdf))

        # Plot polygons:
        glyph = p.multi_polygons(xs="__x__",
                                 ys="__y__",
                                 source=geo_source,
                                 legend=legend,
                                 **kwargs)

    # Add hovertool:
    if hovertool and (category_options == 1 or len(hovertool_columns) > 0):
        my_hover = HoverTool(renderers=[glyph])
        if hovertool_string is None:
            my_hover.tooltips = [(str(col), "@{%s}" % col)
                                 for col in hovertool_columns]
        else:
            my_hover.tooltips = hovertool_string
        p.add_tools(my_hover)

    # Add colorbar:
    if show_colorbar and category_options == 1:
        colorbar_options = {
            "color_mapper": colormapper,
            "label_standoff": 12,
            "border_line_color": None,
            "location": (0, 0),
        }
        if colormap_uselog:
            colorbar_options["ticker"] = LogTicker()

        if colorbar_tick_format:
            colorbar_options["formatter"] = get_tick_formatter(
                colorbar_tick_format)

        colorbar = ColorBar(**colorbar_options)

        p.add_layout(colorbar, "right")

    # Add Dropdown Widget:
    if not dropdown is None:
        # Define Dropdown widget:
        dropdown_widget = Dropdown(label="Select Choropleth Layer",
                                   menu=list(zip(dropdown, dropdown)))

        # Define Callback for Dropdown widget:
        callback = CustomJS(
            args=dict(
                dropdown_widget=dropdown_widget,
                geo_source=geo_source,
                legend=p.legend[0].items[0],
            ),
            code="""

                //Change selection of field for Colormapper for choropleth plot:
                geo_source.data["Colormap"] = geo_source.data[dropdown_widget.value];
                geo_source.change.emit();

                //Change label of Legend:
                legend.label["value"] = " " + dropdown_widget.value;

                            """,
        )
        dropdown_widget.js_on_change("value", callback)

        # Add Dropdown widget above the plot:
        if old_layout is None:
            layout = column(dropdown_widget, p)
        else:
            layout = column(dropdown_widget, old_layout)

    # Add Slider Widget:
    if not slider is None:

        if slider_range is None:
            slider_start = 0
            slider_end = len(slider) - 1
            slider_step = 1

        value2name = ColumnDataSource({
            "Values":
            np.arange(slider_start, slider_end + slider_step, slider_step),
            "Names":
            slider,
        })

        # Define Slider widget:
        slider_widget = Slider(
            start=slider_start,
            end=slider_end,
            value=slider_start,
            step=slider_step,
            title=slider_name,
        )

        # Define Callback for Slider widget:
        callback = CustomJS(
            args=dict(
                slider_widget=slider_widget,
                geo_source=geo_source,
                value2name=value2name,
            ),
            code="""

                //Change selection of field for Colormapper for choropleth plot:
                var slider_value = slider_widget.value;
                for(i=0; i<value2name.data["Names"].length; i++)
                    {
                    if (value2name.data["Values"][i] == slider_value)
                        {
                         var name = value2name.data["Names"][i];
                         }

                    }
                geo_source.data["Colormap"] = geo_source.data[name];
                geo_source.change.emit();

                            """,
        )
        slider_widget.js_on_change("value", callback)

        # Add Slider widget above the plot:
        if old_layout is None:
            layout = column(slider_widget, p)
        else:
            layout = column(slider_widget, old_layout)

    # Hide legend if user wants:
    if legend_input is False:
        p.legend.visible = False

    # Set click policy for legend:
    p.legend.click_policy = "hide"

    # Set panning option:
    if panning is False:
        p.toolbar.active_drag = None

    # Set zooming option:
    if zooming is False:
        p.toolbar.active_scroll = None

    # Display plot and if wanted return plot:
    if layout is None:
        if old_layout is None:
            layout = p
        else:
            layout = old_layout

    # Display plot if wanted
    if show_figure:
        show(layout)

    # Return as (embeddable) HTML if wanted:
    if return_html:
        return embedded_html(layout)

    # Return plot:
    if return_figure:
        return layout
Пример #23
0
def create(palm):
    energy_min = palm.energy_range.min()
    energy_max = palm.energy_range.max()
    energy_npoints = palm.energy_range.size

    current_results = (0, 0, 0, 0)

    doc = curdoc()

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

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

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

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

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

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

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

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

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

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

    # ---- line glyphs
    xcorr_source = ColumnDataSource(dict(lags=[], xcorr1=[], xcorr2=[]))
    xcorr_plot.add_glyph(
        xcorr_source,
        Line(x='lags', y='xcorr1', line_color='purple', line_dash='dashed'))
    xcorr_plot.add_glyph(xcorr_source,
                         Line(x='lags', y='xcorr2', line_color='purple'))

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

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

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

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

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

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

    # ---- vertical span
    pulse_delay_plot_span = Span(location=0, dimension='height')
    pulse_delay_plot.add_layout(pulse_delay_plot_span)

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

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

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

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

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

    # ---- vertical span
    pulse_length_plot_span = Span(location=0, dimension='height')
    pulse_length_plot.add_layout(pulse_length_plot_span)

    # Folder path text input
    def path_textinput_callback(_attr, _old, new):
        save_textinput.value = new
        path_periodic_update()

    path_textinput = TextInput(title="Folder Path:",
                               value=os.path.join(os.path.expanduser('~')),
                               width=510)
    path_textinput.on_change('value', path_textinput_callback)

    # Saved runs dropdown menu
    def h5_update(pulse, delays, debug_data):
        prep_data, lags, corr_res_uncut, corr_results = debug_data

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

        xcorr_source.data.update(lags=lags,
                                 xcorr1=corr_res_uncut[pulse, :],
                                 xcorr2=corr_results[pulse, :])

        xcorr_center_span.location = delays[pulse]
        pulse_delay_plot_span.location = pulse
        pulse_length_plot_span.location = pulse

    # this placeholder function should be reassigned in 'saved_runs_dropdown_callback'
    h5_update_fun = lambda pulse: None

    def saved_runs_dropdown_callback(_attr, _old, new):
        if new != "Saved Runs":
            nonlocal h5_update_fun, current_results
            saved_runs_dropdown.label = new
            filepath = os.path.join(path_textinput.value, new)
            tags, delays, lengths, debug_data = palm.process_hdf5_file(
                filepath, debug=True)
            current_results = (new, tags, delays, lengths)

            if autosave_checkbox.active:
                save_button_callback()

            pulse_delay_source.data.update(pulse=np.arange(len(delays)),
                                           delay=delays)
            pulse_length_source.data.update(x=np.arange(len(lengths)),
                                            y=lengths)
            h5_update_fun = partial(h5_update,
                                    delays=delays,
                                    debug_data=debug_data)

            pulse_slider.end = len(delays) - 1
            pulse_slider.value = 0
            h5_update_fun(0)

    saved_runs_dropdown = Dropdown(label="Saved Runs",
                                   button_type='primary',
                                   menu=[])
    saved_runs_dropdown.on_change('value', saved_runs_dropdown_callback)

    # ---- saved run periodic update
    def path_periodic_update():
        new_menu = []
        if os.path.isdir(path_textinput.value):
            for entry in os.scandir(path_textinput.value):
                if entry.is_file() and entry.name.endswith(('.hdf5', '.h5')):
                    new_menu.append((entry.name, entry.name))
        saved_runs_dropdown.menu = sorted(new_menu, reverse=True)

    doc.add_periodic_callback(path_periodic_update, 5000)

    # Pulse number slider
    def pulse_slider_callback(_attr, _old, new):
        h5_update_fun(pulse=new)

    pulse_slider = Slider(
        start=0,
        end=99999,
        value=0,
        step=1,
        title="Pulse ID",
        callback_policy='throttle',
        callback_throttle=500,
    )
    pulse_slider.on_change('value', pulse_slider_callback)

    # Energy maximal range value text input
    def energy_max_textinput_callback(_attr, old, new):
        nonlocal energy_max
        try:
            new_value = float(new)
            if new_value > energy_min:
                energy_max = new_value
                palm.energy_range = np.linspace(energy_min, energy_max,
                                                energy_npoints)
                saved_runs_dropdown_callback('', '', saved_runs_dropdown.label)
            else:
                energy_max_textinput.value = old

        except ValueError:
            energy_max_textinput.value = old

    energy_max_textinput = TextInput(title='Maximal Energy, eV:',
                                     value=str(energy_max))
    energy_max_textinput.on_change('value', energy_max_textinput_callback)

    # Energy minimal range value text input
    def energy_min_textinput_callback(_attr, old, new):
        nonlocal energy_min
        try:
            new_value = float(new)
            if new_value < energy_max:
                energy_min = new_value
                palm.energy_range = np.linspace(energy_min, energy_max,
                                                energy_npoints)
                saved_runs_dropdown_callback('', '', saved_runs_dropdown.label)
            else:
                energy_min_textinput.value = old

        except ValueError:
            energy_min_textinput.value = old

    energy_min_textinput = TextInput(title='Minimal Energy, eV:',
                                     value=str(energy_min))
    energy_min_textinput.on_change('value', energy_min_textinput_callback)

    # Energy number of interpolation points text input
    def energy_npoints_textinput_callback(_attr, old, new):
        nonlocal energy_npoints
        try:
            new_value = int(new)
            if new_value > 1:
                energy_npoints = new_value
                palm.energy_range = np.linspace(energy_min, energy_max,
                                                energy_npoints)
                saved_runs_dropdown_callback('', '', saved_runs_dropdown.label)
            else:
                energy_npoints_textinput.value = old

        except ValueError:
            energy_npoints_textinput.value = old

    energy_npoints_textinput = TextInput(
        title='Number of interpolation points:', value=str(energy_npoints))
    energy_npoints_textinput.on_change('value',
                                       energy_npoints_textinput_callback)

    # Save location
    save_textinput = TextInput(title="Save Folder Path:",
                               value=os.path.join(os.path.expanduser('~')))

    # Autosave checkbox
    autosave_checkbox = CheckboxButtonGroup(labels=["Auto Save"],
                                            active=[],
                                            width=250)

    # Save button
    def save_button_callback():
        if current_results[0]:
            filename, tags, delays, lengths = current_results
            save_filename = os.path.splitext(filename)[0] + '.csv'
            df = pd.DataFrame({
                'pulse_id': tags,
                'pulse_delay': delays,
                'pulse_length': lengths
            })
            df.to_csv(os.path.join(save_textinput.value, save_filename),
                      index=False)

    save_button = Button(label="Save Results",
                         button_type='default',
                         width=250)
    save_button.on_click(save_button_callback)

    # assemble
    tab_layout = column(
        row(
            column(waveform_plot, xcorr_plot),
            Spacer(width=30),
            column(
                path_textinput,
                saved_runs_dropdown,
                pulse_slider,
                Spacer(height=30),
                energy_min_textinput,
                energy_max_textinput,
                energy_npoints_textinput,
                Spacer(height=30),
                save_textinput,
                autosave_checkbox,
                save_button,
            ),
        ),
        row(pulse_delay_plot, Spacer(width=10), pulse_length_plot),
    )

    return Panel(child=tab_layout, title="HDF5 File")
Пример #24
0
def geoplot(gdf_in,
            fig=None,
            figsize=None,
            title="",
            xlabel="Longitude",
            ylabel="Latitude",
            color="blue",
            colormap=None,
            colormap_uselog=False,
            colormap_range=None,
            category=None,
            dropdown=None,
            slider=None,
            slider_range=None,
            slider_name="",
            show_colorbar=True,
            xrange=None,
            yrange=None,
            hovertool=True,
            hovertool_columns=[],
            simplify_shapes=None,
            tile_provider="CARTODBPOSITRON_RETINA",
            tile_provider_url=None,
            toolbar_location="right",
            show_figure=True,
            return_figure=True,
            return_html=False,
            legend=True,
            webgl=True,
            **kwargs):
    """Doc-String: TODO"""

    gdf = gdf_in.copy()

    # Check layertypes:
    layertypes = []
    if "Point" in str(gdf.geom_type.unique()):
        layertypes.append("Point")
    if "Line" in str(gdf.geom_type.unique()):
        layertypes.append("Line")
    if "Polygon" in str(gdf.geom_type.unique()):
        layertypes.append("Polygon")
    if len(layertypes) > 1:
        raise Exception(
            "Can only plot GeoDataFrames/Series with single type of geometry (either Point, Line or Polygon). Provided is a GeoDataFrame/Series with types: %s"
            % layertypes)

    # Get and check provided parameters for geoplot:
    figure_options = {
        "title": title,
        "x_axis_label": xlabel,
        "y_axis_label": ylabel,
        "plot_width": 600,
        "plot_height": 400,
        "toolbar_location": toolbar_location,
        "active_scroll": "wheel_zoom",
    }
    if not isinstance(figsize, type(None)):
        width, height = figsize
        figure_options["plot_width"] = width
        figure_options["plot_height"] = height
    if webgl:
        figure_options["output_backend"] = "webgl"

    if not isinstance(fig, type(None)):
        raise NotImplementedError("Parameter <figure> not yet implemented.")

    # Convert GeoDataFrame to Web Mercador Projection:
    gdf.to_crs({"init": "epsg:3857"}, inplace=True)

    # Simplify shapes if wanted:
    if isinstance(simplify_shapes, numbers.Number):
        if layertypes[0] in ["Line", "Polygon"]:
            gdf["geometry"] = gdf["geometry"].simplify(simplify_shapes)
    elif not isinstance(simplify_shapes, type(None)):
        raise ValueError(
            "<simplify_shapes> parameter only accepts numbers or None.")

    # Check for category, dropdown or slider (choropleth map column):
    category_options = 0
    if not isinstance(category, type(None)):
        category_options += 1
        category_columns = [category]
    if not isinstance(dropdown, type(None)):
        category_options += 1
        category_columns = dropdown
    if not isinstance(slider, type(None)):
        category_options += 1
        category_columns = slider
    if category_options > 1:
        raise ValueError(
            "Only one of <category>, <dropdown> or <slider> parameters is allowed to be used at once."
        )

    # Check for category (single choropleth plot):
    if isinstance(category, type(None)):
        pass
    elif isinstance(category, (list, tuple)):
        raise ValueError(
            "For <category>, please provide an existing single column of the GeoDataFrame."
        )
    elif category in gdf.columns:
        pass
    else:
        raise ValueError(
            "Could not find column '%s' in GeoDataFrame. For <category>, please provide an existing single column of the GeoDataFrame."
            % category)

    # Check for dropdown (multiple choropleth plots via dropdown selection):
    if isinstance(dropdown, type(None)):
        pass
    elif not isinstance(dropdown, (list, tuple)):
        raise ValueError(
            "For <dropdown>, please provide a list/tuple of existing columns of the GeoDataFrame."
        )
    else:
        for col in dropdown:
            if col not in gdf.columns:
                raise ValueError(
                    "Could not find column '%s' for <dropdown> in GeoDataFrame. "
                    % col)

    # Check for slider (multiple choropleth plots via slider selection):
    if isinstance(slider, type(None)):
        pass
    elif not isinstance(slider, (list, tuple)):
        raise ValueError(
            "For <slider>, please provide a list/tuple of existing columns of the GeoDataFrame."
        )
    else:
        for col in slider:
            if col not in gdf.columns:
                raise ValueError(
                    "Could not find column '%s' for <slider> in GeoDataFrame. "
                    % col)

        if not isinstance(slider_range, type(None)):
            if not isinstance(slider_range, Iterable):
                raise ValueError(
                    "<slider_range> has to be a type that is iterable like list, tuple, range, ..."
                )
            else:
                slider_range = list(slider_range)
                if len(slider_range) != len(slider):
                    raise ValueError(
                        "The number of elements in <slider_range> has to be the same as in <slider>."
                    )
                steps = []
                for i in range(len(slider_range) - 1):
                    steps.append(slider_range[i + 1] - slider_range[i])

                if len(set(steps)) > 1:
                    raise ValueError(
                        "<slider_range> has to have equal step size between each elements (like a range-object)."
                    )
                else:
                    slider_step = steps[0]
                    slider_start = slider_range[0]
                    slider_end = slider_range[-1]

    # Check colormap if either <category>, <dropdown> or <slider> is choosen:
    if category_options == 1:
        if isinstance(colormap, type(None)):
            colormap = blue_colormap
        elif isinstance(colormap, (tuple, list)):
            if len(colormap) > 1:
                pass
            else:
                raise ValueError(
                    "<colormap> only accepts a list/tuple of at least two colors or the name of one of the following predefined colormaps (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s"
                    % (list(all_palettes.keys())))
        elif isinstance(colormap, str):
            if colormap in all_palettes:
                colormap = all_palettes[colormap]
                colormap = colormap[max(colormap.keys())]
            else:
                raise ValueError(
                    "Could not find <colormap> with name %s. The following predefined colormaps are supported (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s"
                    % (colormap, list(all_palettes.keys())))
        else:
            raise ValueError(
                "<colormap> only accepts a list/tuple of at least two colors or the name of one of the following predefined colormaps (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s"
                % (list(all_palettes.keys())))
    else:
        if isinstance(color, str):
            colormap = [color]
        else:
            raise ValueError(
                "<color> has to be a string specifying the fill_color of the map glyph."
            )

    # Create Figure to draw:
    p = figure(x_axis_type="mercator",
               y_axis_type="mercator",
               **figure_options)

    # Get ridd of zoom on axes:
    for t in p.tools:
        if type(t) == WheelZoomTool:
            t.zoom_on_axis = False

    # Add Tile Source as Background:
    p = _add_backgroundtile(p, tile_provider, tile_provider_url)

    # Hide legend if wanted:
    if not legend:
        p.legend.visible = False
    elif isinstance(legend, str):
        pass
    else:
        legend = "GeoLayer"

    # Define colormapper:
    if len(colormap) == 1:
        kwargs["fill_color"] = colormap[0]

    elif not isinstance(category, type(None)):
        # Check if category column is numerical:
        if not issubclass(gdf[category].dtype.type, np.number):
            raise NotImplementedError(
                "<category> plot only yet implemented for numerical columns. Column '%s' is not numerical."
                % category)

        field = category
        colormapper_options = {"palette": colormap}
        if not isinstance(colormap_range, type(None)):
            if not isinstance(colormap_range, (tuple, list)):
                raise ValueError(
                    "<colormap_range> can only be 'None' or a tuple/list of form (min, max)."
                )
            elif len(colormap_range) == 2:
                colormapper_options["low"] = colormap_range[0]
                colormapper_options["high"] = colormap_range[1]
        else:
            colormapper_options["low"] = gdf[field].min()
            colormapper_options["high"] = gdf[field].max()
        if colormap_uselog:
            colormapper = LogColorMapper(**colormapper_options)
        else:
            colormapper = LinearColorMapper(**colormapper_options)
        kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper}
        if not isinstance(legend, str):
            legend = str(field)

    elif not isinstance(dropdown, type(None)):
        # Check if all columns in dropdown selection are numerical:
        for col in dropdown:
            if not issubclass(gdf[col].dtype.type, np.number):
                raise NotImplementedError(
                    "<dropdown> plot only yet implemented for numerical columns. Column '%s' is not numerical."
                    % col)

        field = dropdown[0]
        colormapper_options = {"palette": colormap}
        if not isinstance(colormap_range, type(None)):
            if not isinstance(colormap_range, (tuple, list)):
                raise ValueError(
                    "<colormap_range> can only be 'None' or a tuple/list of form (min, max)."
                )
            elif len(colormap_range) == 2:
                colormapper_options["low"] = colormap_range[0]
                colormapper_options["high"] = colormap_range[1]
        else:
            colormapper_options["low"] = gdf[dropdown].min().min()
            colormapper_options["high"] = gdf[dropdown].max().max()
        if colormap_uselog:
            colormapper = LogColorMapper(**colormapper_options)
        else:
            colormapper = LinearColorMapper(**colormapper_options)
        kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper}
        if not isinstance(legend, str):
            legend = "Geolayer"

    elif not isinstance(slider, type(None)):
        # Check if all columns in dropdown selection are numerical:
        for col in slider:
            if not issubclass(gdf[col].dtype.type, np.number):
                raise NotImplementedError(
                    "<slider> plot only yet implemented for numerical columns. Column '%s' is not numerical."
                    % col)

        field = slider[0]
        colormapper_options = {"palette": colormap}
        if not isinstance(colormap_range, type(None)):
            if not isinstance(colormap_range, (tuple, list)):
                raise ValueError(
                    "<colormap_range> can only be 'None' or a tuple/list of form (min, max)."
                )
            elif len(colormap_range) == 2:
                colormapper_options["low"] = colormap_range[0]
                colormapper_options["high"] = colormap_range[1]
        else:
            colormapper_options["low"] = gdf[slider].min().min()
            colormapper_options["high"] = gdf[slider].max().max()
        if colormap_uselog:
            colormapper = LogColorMapper(**colormapper_options)
        else:
            colormapper = LinearColorMapper(**colormapper_options)
        kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper}
        if not isinstance(legend, str):
            legend = "Geolayer"

    # Check for Hovertool columns:
    if hovertool:
        if not isinstance(hovertool_columns, (list, tuple)):
            if hovertool_columns == "all":
                hovertool_columns = list(
                    filter(lambda col: col != "geometry", df_shapes.columns))
            else:
                raise ValueError(
                    "<hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'."
                )
        elif len(hovertool_columns) == 0:
            if not isinstance(category, type(None)):
                hovertool_columns = [category]
            elif not isinstance(dropdown, type(None)):
                hovertool_columns = dropdown
            elif not isinstance(slider, type(None)):
                hovertool_columns = slider
            else:
                hovertool_columns = []
        else:
            for col in hovertool_columns:
                if col not in gdf.columns:
                    raise ValueError(
                        "Could not find columns '%s' in GeoDataFrame. <hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'."
                        % col)
    else:
        if isinstance(category, type(None)):
            hovertool_columns = []
        else:
            hovertool_columns = [category]

    # Reduce DataFrame to needed columns:
    additional_columns = []
    for kwarg, value in kwargs.items():
        if isinstance(value, Hashable):
            if value in gdf.columns:
                additional_columns.append(value)
    if category_options == 0:
        gdf = gdf[list(set(hovertool_columns) | set(additional_columns)) +
                  ["geometry"]]
    else:
        gdf = gdf[list(
            set(hovertool_columns) | set(category_columns)
            | set(additional_columns)) + ["geometry"]]
        gdf["Colormap"] = gdf[field]
        field = "Colormap"

    # Create GeoJSON DataSource for Plot:
    geo_source = GeoJSONDataSource(geojson=gdf.to_json())

    # Draw Glyph on Figure:
    layout = None
    if "Point" in layertypes:
        if "line_color" not in kwargs:
            kwargs["line_color"] = kwargs["fill_color"]
        p.scatter(x="x", y="y", source=geo_source, legend=legend, **kwargs)

    if "Line" in layertypes:
        if "line_color" not in kwargs:
            kwargs["line_color"] = kwargs["fill_color"]
            del kwargs["fill_color"]
        p.multi_line(xs="xs",
                     ys="ys",
                     source=geo_source,
                     legend=legend,
                     **kwargs)

    if "Polygon" in layertypes:

        if "line_color" not in kwargs:
            kwargs["line_color"] = "black"

        # Plot polygons:
        p.patches(xs="xs", ys="ys", source=geo_source, legend=legend, **kwargs)

    if hovertool and (category_options == 1 or len(hovertool_columns) > 0):
        my_hover = HoverTool()
        my_hover.tooltips = [(str(col), "@{%s}" % col)
                             for col in hovertool_columns]
        p.add_tools(my_hover)

    if show_colorbar and category_options == 1:
        colorbar_options = {
            "color_mapper": colormapper,
            "label_standoff": 12,
            "border_line_color": None,
            "location": (0, 0),
        }
        if colormap_uselog:
            colorbar_options["ticker"] = LogTicker()

        colorbar = ColorBar(**colorbar_options)

        p.add_layout(colorbar, "right")

    if not isinstance(dropdown, type(None)):
        # Define Dropdown widget:
        dropdown_widget = Dropdown(label="Select Choropleth Layer",
                                   menu=list(zip(dropdown, dropdown)))

        # Define Callback for Dropdown widget:
        callback = CustomJS(
            args=dict(dropdown_widget=dropdown_widget,
                      geo_source=geo_source,
                      p=p),
            code="""

                //Change selection of field for Colormapper for choropleth plot:
                geo_source.data["Colormap"] = geo_source.data[dropdown_widget.value];
                geo_source.change.emit();
                //p.legend[0].items[0]["label"] = dropdown_widget.value;

                            """,
        )
        dropdown_widget.js_on_change("value", callback)

        # Add Dropdown widget above the plot:
        layout = column(dropdown_widget, p)

    if not isinstance(slider, type(None)):

        if slider_range is None:
            slider_start = 0
            slider_end = len(slider) - 1
            slider_step = 1

        value2name = ColumnDataSource({
            "Values":
            np.arange(slider_start, slider_end + slider_step, slider_step),
            "Names":
            slider,
        })

        # Define Slider widget:
        slider_widget = Slider(
            start=slider_start,
            end=slider_end,
            value=slider_start,
            step=slider_step,
            title=slider_name,
        )

        # Define Callback for Slider widget:
        callback = CustomJS(
            args=dict(
                slider_widget=slider_widget,
                geo_source=geo_source,
                value2name=value2name,
            ),
            code="""

                //Change selection of field for Colormapper for choropleth plot:
                var slider_value = slider_widget.value;
                for(i=0; i<value2name.data["Names"].length; i++)
                    {
                    if (value2name.data["Values"][i] == slider_value)
                        {
                         var name = value2name.data["Names"][i];
                         }

                    }
                geo_source.data["Colormap"] = geo_source.data[name];
                geo_source.change.emit();

                            """,
        )
        slider_widget.js_on_change("value", callback)

        # Add Slider widget above the plot:
        layout = column(slider_widget, p)

    # Set click policy for legend:
    p.legend.click_policy = "hide"

    # Display plot and if wanted return plot:
    if isinstance(layout, type(None)):
        layout = p

    # Display plot if wanted
    if show_figure:
        show(layout)

    # Return as (embeddable) HTML if wanted:
    if return_html:
        return embedded_html(layout)

    # Return plot:
    if return_figure:
        return layout
Пример #25
0
    def bkapp(doc):
        def fitButtonCallback(new):
            x_range = [p.x_range.start, p.x_range.end]
            y_range = [p.y_range.start, p.y_range.end]
            x_inds = np.where(np.logical_and(bin_mids >= x_range[0], bin_mids <= x_range[1]), bin_mids, np.nan)
            x = x_inds[np.isfinite(x_inds)]
            y = hist[np.isfinite(x_inds)]
            x_fit = np.linspace(x_range[0], x_range[1], 10000)
            p0 = [y.max(), x[np.argmax(y)], x[np.argmax(y)], 0]
            p_fit = curve_fit(_gaus, x, y, p0, ftol=1e-2)[0]
            y_fit = _gaus(x_fit, *p_fit)
            # p.line(x_fit, y_fit, legend_label='Amp. = %5.2f, Mean = %5.2f, Std. = %5.2f, Base = %5.2f' % p_fit)
            fitDataSource.patch(dict(x=[(slice(0, len(x_fit)), x_fit)], y=[(slice(0, len(y_fit)), y_fit)]))
            fit_str = 'Amp. = %5.2f, Mean = %5.2f, Std. = %5.2f, Base = %5.2f' % (p_fit[0], p_fit[1], p_fit[2], p_fit[3])
            #p.line(x=x_fit, y=y_fit, legend_label=new_data['legend_label'], color='black')
            p.x_range = Range1d(bins[0], bins[-1])
            p.y_range = Range1d(0, hists[0].max())
            p.legend.items = [(settings['sz_key'], [hist_line]), (fit_str, [fit_line])]

        def SzDropDownCallback(new):
            settings['sz_key'] = new.item
            bins = np.linspace(0, 130000, 1000)
            size_datasets = pysp2.util.calc_diams_masses(my_cal_data[settings['sz_key']])
            hist, bins = np.histogram(size_datasets[settings['variable']].where(size_datasets.ScatRejectKey == 0).values,
                                      bins=bins)
            histDataSource.patch(dict(x=[(slice(0, len(bin_mids)), bin_mids)], y=[(slice(0, len(hist)), hist)]))
            p.legend.items = [(settings['sz_key'], [hist_line]), ("", [fit_line])]

        def VariableCallback(new):
            settings['variable'] = new.item
            bins = np.linspace(0, 130000, 1000)
            size_datasets = pysp2.util.calc_diams_masses(my_cal_data[settings['sz_key']])
            hist, bins = np.histogram(size_datasets[settings['variable']].where(size_datasets.ScatRejectKey == 0).values,
                                      bins=bins)
            histDataSource.patch(dict(x=[(slice(0, len(bin_mids)), bin_mids)], y=[(slice(0, len(hist)), hist)]))
            p.legend.items = [(settings['sz_key'], [hist_line]), ("", [fit_line])]

        settings = {'sz_key':'scat_200', 'variable': 'PkHt_ch0'}

        sizes = np.array([350, 300, 269, 220, 200, 170])
        scat_mean = np.ones_like(sizes, dtype=float)

        size_datasets = {}
        i = 0
        # bins = np.logspace(-13, -10, 120)
        bins = np.linspace(0, 130000, 1000)
        hists = np.zeros((len(sizes), len(bins) - 1))
        size_datasets = pysp2.util.calc_diams_masses(my_cal_data[settings['sz_key']])
        hist, bins = np.histogram(size_datasets[settings['variable']].where(size_datasets.ScatRejectKey == 0).values,
                                  bins=bins)
        p = figure(tools="pan,box_zoom,reset,save", x_range=(bins[0], bins[-1]), y_range=(0, hist.max()))
        bin_mids = (bins[:-1] + bins[1:])/2.0
        histDataSource = ColumnDataSource(data=dict(x=bin_mids, y=hist))
        x_fit = np.linspace(0, 130000, 10000)
        fitDataSource = ColumnDataSource(data=dict(x=x_fit, y=np.nan*np.ones_like(x_fit)))
        hist_line = p.line('x', 'y', source=histDataSource, legend_label=settings['sz_key'])
        fit_line = p.line('x', 'y', source=fitDataSource, legend_label="")
        button = Button(label="Curve fit")
        button.on_click(fitButtonCallback)
        size_menu = []
        for keys in my_cal_data.keys():
            size_menu.append((keys, keys))
        SzDropDown = Dropdown(label="Calibration data", menu=size_menu)
        SzDropDown.on_click(SzDropDownCallback)
        vars = ["PkHt_ch0", "PkHt_ch1", "PkHt_ch4", "PkHt_ch5", "FtAmp_ch0", 'FtAmp_ch4']
        vars = [(x, x) for x in vars]
        VarDropDown = Dropdown(label="Variable", menu=vars)
        VarDropDown.on_click(VariableCallback)
        doc.add_root(column(button, SzDropDown, VarDropDown, p))
Пример #26
0
def create():
    det_data = {}
    fit_params = {}
    js_data = ColumnDataSource(
        data=dict(content=["", ""], fname=["", ""], ext=["", ""]))

    def proposal_textinput_callback(_attr, _old, new):
        proposal = new.strip()
        for zebra_proposals_path in pyzebra.ZEBRA_PROPOSALS_PATHS:
            proposal_path = os.path.join(zebra_proposals_path, proposal)
            if os.path.isdir(proposal_path):
                # found it
                break
        else:
            raise ValueError(f"Can not find data for proposal '{proposal}'.")

        file_list = []
        for file in os.listdir(proposal_path):
            if file.endswith((".ccl", ".dat")):
                file_list.append((os.path.join(proposal_path, file), file))
        file_select.options = file_list
        file_open_button.disabled = False
        file_append_button.disabled = False

    proposal_textinput = TextInput(title="Proposal number:", width=210)
    proposal_textinput.on_change("value", proposal_textinput_callback)

    def _init_datatable():
        scan_list = [s["idx"] for s in det_data]
        hkl = [f'{s["h"]} {s["k"]} {s["l"]}' for s in det_data]
        export = [s.get("active", True) for s in det_data]
        scan_table_source.data.update(
            scan=scan_list,
            hkl=hkl,
            fit=[0] * len(scan_list),
            export=export,
        )
        scan_table_source.selected.indices = []
        scan_table_source.selected.indices = [0]

        merge_options = [(str(i), f"{i} ({idx})")
                         for i, idx in enumerate(scan_list)]
        merge_from_select.options = merge_options
        merge_from_select.value = merge_options[0][0]

    file_select = MultiSelect(title="Available .ccl/.dat files:",
                              width=210,
                              height=250)

    def file_open_button_callback():
        nonlocal det_data
        det_data = []
        for f_name in file_select.value:
            with open(f_name) as file:
                base, ext = os.path.splitext(f_name)
                if det_data:
                    append_data = pyzebra.parse_1D(file, ext)
                    pyzebra.normalize_dataset(append_data,
                                              monitor_spinner.value)
                    pyzebra.merge_datasets(det_data, append_data)
                else:
                    det_data = pyzebra.parse_1D(file, ext)
                    pyzebra.normalize_dataset(det_data, monitor_spinner.value)
                    pyzebra.merge_duplicates(det_data)
                    js_data.data.update(fname=[base, base])

        _init_datatable()
        append_upload_button.disabled = False

    file_open_button = Button(label="Open New", width=100, disabled=True)
    file_open_button.on_click(file_open_button_callback)

    def file_append_button_callback():
        for f_name in file_select.value:
            with open(f_name) as file:
                _, ext = os.path.splitext(f_name)
                append_data = pyzebra.parse_1D(file, ext)

            pyzebra.normalize_dataset(append_data, monitor_spinner.value)
            pyzebra.merge_datasets(det_data, append_data)

        _init_datatable()

    file_append_button = Button(label="Append", width=100, disabled=True)
    file_append_button.on_click(file_append_button_callback)

    def upload_button_callback(_attr, _old, new):
        nonlocal det_data
        det_data = []
        for f_str, f_name in zip(new, upload_button.filename):
            with io.StringIO(base64.b64decode(f_str).decode()) as file:
                base, ext = os.path.splitext(f_name)
                if det_data:
                    append_data = pyzebra.parse_1D(file, ext)
                    pyzebra.normalize_dataset(append_data,
                                              monitor_spinner.value)
                    pyzebra.merge_datasets(det_data, append_data)
                else:
                    det_data = pyzebra.parse_1D(file, ext)
                    pyzebra.normalize_dataset(det_data, monitor_spinner.value)
                    pyzebra.merge_duplicates(det_data)
                    js_data.data.update(fname=[base, base])

        _init_datatable()
        append_upload_button.disabled = False

    upload_div = Div(text="or upload new .ccl/.dat files:",
                     margin=(5, 5, 0, 5))
    upload_button = FileInput(accept=".ccl,.dat", multiple=True, width=200)
    upload_button.on_change("value", upload_button_callback)

    def append_upload_button_callback(_attr, _old, new):
        for f_str, f_name in zip(new, append_upload_button.filename):
            with io.StringIO(base64.b64decode(f_str).decode()) as file:
                _, ext = os.path.splitext(f_name)
                append_data = pyzebra.parse_1D(file, ext)

            pyzebra.normalize_dataset(append_data, monitor_spinner.value)
            pyzebra.merge_datasets(det_data, append_data)

        _init_datatable()

    append_upload_div = Div(text="append extra files:", margin=(5, 5, 0, 5))
    append_upload_button = FileInput(accept=".ccl,.dat",
                                     multiple=True,
                                     width=200,
                                     disabled=True)
    append_upload_button.on_change("value", append_upload_button_callback)

    def monitor_spinner_callback(_attr, old, new):
        if det_data:
            pyzebra.normalize_dataset(det_data, new)
            _update_plot(_get_selected_scan())

    monitor_spinner = Spinner(title="Monitor:",
                              mode="int",
                              value=100_000,
                              low=1,
                              width=145)
    monitor_spinner.on_change("value", monitor_spinner_callback)

    def _update_table():
        fit_ok = [(1 if "fit" in scan else 0) for scan in det_data]
        scan_table_source.data.update(fit=fit_ok)

    def _update_plot(scan):
        scan_motor = scan["scan_motor"]

        y = scan["counts"]
        x = scan[scan_motor]

        plot.axis[0].axis_label = scan_motor
        plot_scatter_source.data.update(x=x,
                                        y=y,
                                        y_upper=y + np.sqrt(y),
                                        y_lower=y - np.sqrt(y))

        fit = scan.get("fit")
        if fit is not None:
            x_fit = np.linspace(x[0], x[-1], 100)
            plot_fit_source.data.update(x=x_fit, y=fit.eval(x=x_fit))

            x_bkg = []
            y_bkg = []
            xs_peak = []
            ys_peak = []
            comps = fit.eval_components(x=x_fit)
            for i, model in enumerate(fit_params):
                if "linear" in model:
                    x_bkg = x_fit
                    y_bkg = comps[f"f{i}_"]

                elif any(val in model
                         for val in ("gaussian", "voigt", "pvoigt")):
                    xs_peak.append(x_fit)
                    ys_peak.append(comps[f"f{i}_"])

            plot_bkg_source.data.update(x=x_bkg, y=y_bkg)
            plot_peak_source.data.update(xs=xs_peak, ys=ys_peak)

            fit_output_textinput.value = fit.fit_report()

        else:
            plot_fit_source.data.update(x=[], y=[])
            plot_bkg_source.data.update(x=[], y=[])
            plot_peak_source.data.update(xs=[], ys=[])
            fit_output_textinput.value = ""

    # Main plot
    plot = Plot(
        x_range=DataRange1d(),
        y_range=DataRange1d(only_visible=True),
        plot_height=470,
        plot_width=700,
    )

    plot.add_layout(LinearAxis(axis_label="Counts"), place="left")
    plot.add_layout(LinearAxis(axis_label="Scan motor"), place="below")

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

    plot_scatter_source = ColumnDataSource(
        dict(x=[0], y=[0], y_upper=[0], y_lower=[0]))
    plot_scatter = plot.add_glyph(
        plot_scatter_source, Scatter(x="x", y="y", line_color="steelblue"))
    plot.add_layout(
        Whisker(source=plot_scatter_source,
                base="x",
                upper="y_upper",
                lower="y_lower"))

    plot_fit_source = ColumnDataSource(dict(x=[0], y=[0]))
    plot_fit = plot.add_glyph(plot_fit_source, Line(x="x", y="y"))

    plot_bkg_source = ColumnDataSource(dict(x=[0], y=[0]))
    plot_bkg = plot.add_glyph(
        plot_bkg_source,
        Line(x="x", y="y", line_color="green", line_dash="dashed"))

    plot_peak_source = ColumnDataSource(dict(xs=[[0]], ys=[[0]]))
    plot_peak = plot.add_glyph(
        plot_peak_source,
        MultiLine(xs="xs", ys="ys", line_color="red", line_dash="dashed"))

    fit_from_span = Span(location=None, dimension="height", line_dash="dashed")
    plot.add_layout(fit_from_span)

    fit_to_span = Span(location=None, dimension="height", line_dash="dashed")
    plot.add_layout(fit_to_span)

    plot.add_layout(
        Legend(
            items=[
                ("data", [plot_scatter]),
                ("best fit", [plot_fit]),
                ("peak", [plot_peak]),
                ("linear", [plot_bkg]),
            ],
            location="top_left",
            click_policy="hide",
        ))

    plot.add_tools(PanTool(), WheelZoomTool(), ResetTool())
    plot.toolbar.logo = None

    # Scan select
    def scan_table_select_callback(_attr, old, new):
        if not new:
            # skip empty selections
            return

        # Avoid selection of multiple indicies (via Shift+Click or Ctrl+Click)
        if len(new) > 1:
            # drop selection to the previous one
            scan_table_source.selected.indices = old
            return

        if len(old) > 1:
            # skip unnecessary update caused by selection drop
            return

        _update_plot(det_data[new[0]])

    def scan_table_source_callback(_attr, _old, _new):
        _update_preview()

    scan_table_source = ColumnDataSource(
        dict(scan=[], hkl=[], fit=[], export=[]))
    scan_table_source.on_change("data", scan_table_source_callback)

    scan_table = DataTable(
        source=scan_table_source,
        columns=[
            TableColumn(field="scan", title="Scan", width=50),
            TableColumn(field="hkl", title="hkl", width=100),
            TableColumn(field="fit", title="Fit", width=50),
            TableColumn(field="export",
                        title="Export",
                        editor=CheckboxEditor(),
                        width=50),
        ],
        width=310,  # +60 because of the index column
        height=350,
        autosize_mode="none",
        editable=True,
    )

    scan_table_source.selected.on_change("indices", scan_table_select_callback)

    def _get_selected_scan():
        return det_data[scan_table_source.selected.indices[0]]

    merge_from_select = Select(title="scan:", width=145)

    def merge_button_callback():
        scan_into = _get_selected_scan()
        scan_from = det_data[int(merge_from_select.value)]

        if scan_into is scan_from:
            print("WARNING: Selected scans for merging are identical")
            return

        pyzebra.merge_scans(scan_into, scan_from)
        _update_plot(_get_selected_scan())

    merge_button = Button(label="Merge into current", width=145)
    merge_button.on_click(merge_button_callback)

    def restore_button_callback():
        pyzebra.restore_scan(_get_selected_scan())
        _update_plot(_get_selected_scan())

    restore_button = Button(label="Restore scan", width=145)
    restore_button.on_click(restore_button_callback)

    def fit_from_spinner_callback(_attr, _old, new):
        fit_from_span.location = new

    fit_from_spinner = Spinner(title="Fit from:", width=145)
    fit_from_spinner.on_change("value", fit_from_spinner_callback)

    def fit_to_spinner_callback(_attr, _old, new):
        fit_to_span.location = new

    fit_to_spinner = Spinner(title="to:", width=145)
    fit_to_spinner.on_change("value", fit_to_spinner_callback)

    def fitparams_add_dropdown_callback(click):
        # bokeh requires (str, str) for MultiSelect options
        new_tag = f"{click.item}-{fitparams_select.tags[0]}"
        fitparams_select.options.append((new_tag, click.item))
        fit_params[new_tag] = fitparams_factory(click.item)
        fitparams_select.tags[0] += 1

    fitparams_add_dropdown = Dropdown(
        label="Add fit function",
        menu=[
            ("Linear", "linear"),
            ("Gaussian", "gaussian"),
            ("Voigt", "voigt"),
            ("Pseudo Voigt", "pvoigt"),
            # ("Pseudo Voigt1", "pseudovoigt1"),
        ],
        width=145,
    )
    fitparams_add_dropdown.on_click(fitparams_add_dropdown_callback)

    def fitparams_select_callback(_attr, old, new):
        # Avoid selection of multiple indicies (via Shift+Click or Ctrl+Click)
        if len(new) > 1:
            # drop selection to the previous one
            fitparams_select.value = old
            return

        if len(old) > 1:
            # skip unnecessary update caused by selection drop
            return

        if new:
            fitparams_table_source.data.update(fit_params[new[0]])
        else:
            fitparams_table_source.data.update(
                dict(param=[], value=[], vary=[], min=[], max=[]))

    fitparams_select = MultiSelect(options=[], height=120, width=145)
    fitparams_select.tags = [0]
    fitparams_select.on_change("value", fitparams_select_callback)

    def fitparams_remove_button_callback():
        if fitparams_select.value:
            sel_tag = fitparams_select.value[0]
            del fit_params[sel_tag]
            for elem in fitparams_select.options:
                if elem[0] == sel_tag:
                    fitparams_select.options.remove(elem)
                    break

            fitparams_select.value = []

    fitparams_remove_button = Button(label="Remove fit function", width=145)
    fitparams_remove_button.on_click(fitparams_remove_button_callback)

    def fitparams_factory(function):
        if function == "linear":
            params = ["slope", "intercept"]
        elif function == "gaussian":
            params = ["amplitude", "center", "sigma"]
        elif function == "voigt":
            params = ["amplitude", "center", "sigma", "gamma"]
        elif function == "pvoigt":
            params = ["amplitude", "center", "sigma", "fraction"]
        elif function == "pseudovoigt1":
            params = ["amplitude", "center", "g_sigma", "l_sigma", "fraction"]
        else:
            raise ValueError("Unknown fit function")

        n = len(params)
        fitparams = dict(
            param=params,
            value=[None] * n,
            vary=[True] * n,
            min=[None] * n,
            max=[None] * n,
        )

        if function == "linear":
            fitparams["value"] = [0, 1]
            fitparams["vary"] = [False, True]
            fitparams["min"] = [None, 0]

        elif function == "gaussian":
            fitparams["min"] = [0, None, None]

        return fitparams

    fitparams_table_source = ColumnDataSource(
        dict(param=[], value=[], vary=[], min=[], max=[]))
    fitparams_table = DataTable(
        source=fitparams_table_source,
        columns=[
            TableColumn(field="param", title="Parameter"),
            TableColumn(field="value", title="Value", editor=NumberEditor()),
            TableColumn(field="vary", title="Vary", editor=CheckboxEditor()),
            TableColumn(field="min", title="Min", editor=NumberEditor()),
            TableColumn(field="max", title="Max", editor=NumberEditor()),
        ],
        height=200,
        width=350,
        index_position=None,
        editable=True,
        auto_edit=True,
    )

    # start with `background` and `gauss` fit functions added
    fitparams_add_dropdown_callback(types.SimpleNamespace(item="linear"))
    fitparams_add_dropdown_callback(types.SimpleNamespace(item="gaussian"))
    fitparams_select.value = ["gaussian-1"]  # add selection to gauss

    fit_output_textinput = TextAreaInput(title="Fit results:",
                                         width=750,
                                         height=200)

    def proc_all_button_callback():
        for scan, export in zip(det_data, scan_table_source.data["export"]):
            if export:
                pyzebra.fit_scan(scan,
                                 fit_params,
                                 fit_from=fit_from_spinner.value,
                                 fit_to=fit_to_spinner.value)
                pyzebra.get_area(
                    scan,
                    area_method=AREA_METHODS[area_method_radiobutton.active],
                    lorentz=lorentz_checkbox.active,
                )

        _update_plot(_get_selected_scan())
        _update_table()

    proc_all_button = Button(label="Process All",
                             button_type="primary",
                             width=145)
    proc_all_button.on_click(proc_all_button_callback)

    def proc_button_callback():
        scan = _get_selected_scan()
        pyzebra.fit_scan(scan,
                         fit_params,
                         fit_from=fit_from_spinner.value,
                         fit_to=fit_to_spinner.value)
        pyzebra.get_area(
            scan,
            area_method=AREA_METHODS[area_method_radiobutton.active],
            lorentz=lorentz_checkbox.active,
        )

        _update_plot(scan)
        _update_table()

    proc_button = Button(label="Process Current", width=145)
    proc_button.on_click(proc_button_callback)

    area_method_div = Div(text="Intensity:", margin=(5, 5, 0, 5))
    area_method_radiobutton = RadioGroup(labels=["Function", "Area"],
                                         active=0,
                                         width=145)

    lorentz_checkbox = CheckboxGroup(labels=["Lorentz Correction"],
                                     width=145,
                                     margin=(13, 5, 5, 5))

    export_preview_textinput = TextAreaInput(title="Export file preview:",
                                             width=500,
                                             height=400)

    def _update_preview():
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_file = temp_dir + "/temp"
            export_data = []
            for s, export in zip(det_data, scan_table_source.data["export"]):
                if export:
                    export_data.append(s)

            pyzebra.export_1D(
                export_data,
                temp_file,
                export_target_select.value,
                hkl_precision=int(hkl_precision_select.value),
            )

            exported_content = ""
            file_content = []
            for ext in EXPORT_TARGETS[export_target_select.value]:
                fname = temp_file + ext
                if os.path.isfile(fname):
                    with open(fname) as f:
                        content = f.read()
                        exported_content += f"{ext} file:\n" + content
                else:
                    content = ""
                file_content.append(content)

            js_data.data.update(content=file_content)
            export_preview_textinput.value = exported_content

    def export_target_select_callback(_attr, _old, new):
        js_data.data.update(ext=EXPORT_TARGETS[new])
        _update_preview()

    export_target_select = Select(title="Export target:",
                                  options=list(EXPORT_TARGETS.keys()),
                                  value="fullprof",
                                  width=80)
    export_target_select.on_change("value", export_target_select_callback)
    js_data.data.update(ext=EXPORT_TARGETS[export_target_select.value])

    def hkl_precision_select_callback(_attr, _old, _new):
        _update_preview()

    hkl_precision_select = Select(title="hkl precision:",
                                  options=["2", "3", "4"],
                                  value="2",
                                  width=80)
    hkl_precision_select.on_change("value", hkl_precision_select_callback)

    save_button = Button(label="Download File(s)",
                         button_type="success",
                         width=200)
    save_button.js_on_click(
        CustomJS(args={"js_data": js_data}, code=javaScript))

    fitpeak_controls = row(
        column(fitparams_add_dropdown, fitparams_select,
               fitparams_remove_button),
        fitparams_table,
        Spacer(width=20),
        column(fit_from_spinner, lorentz_checkbox, area_method_div,
               area_method_radiobutton),
        column(fit_to_spinner, proc_button, proc_all_button),
    )

    scan_layout = column(
        scan_table,
        row(monitor_spinner, column(Spacer(height=19), restore_button)),
        row(column(Spacer(height=19), merge_button), merge_from_select),
    )

    import_layout = column(
        proposal_textinput,
        file_select,
        row(file_open_button, file_append_button),
        upload_div,
        upload_button,
        append_upload_div,
        append_upload_button,
    )

    export_layout = column(
        export_preview_textinput,
        row(export_target_select, hkl_precision_select,
            column(Spacer(height=19), row(save_button))),
    )

    tab_layout = column(
        row(import_layout, scan_layout, plot, Spacer(width=30), export_layout),
        row(fitpeak_controls, fit_output_textinput),
    )

    return Panel(child=tab_layout, title="ccl integrate")
Пример #27
0
def create(palm):
    doc = curdoc()

    # Calibration averaged waveforms per photon energy
    waveform_plot = Plot(
        title=Title(text="eTOF calibration waveforms"),
        x_range=DataRange1d(),
        y_range=DataRange1d(),
        plot_height=760,
        plot_width=PLOT_CANVAS_WIDTH,
        toolbar_location="right",
    )

    # ---- tools
    waveform_plot.toolbar.logo = None
    waveform_plot_hovertool = HoverTool(
        tooltips=[("energy, eV", "@en"), ("eTOF bin", "$x{0.}")])

    waveform_plot.add_tools(PanTool(), BoxZoomTool(), WheelZoomTool(),
                            ResetTool(), waveform_plot_hovertool)

    # ---- axes
    waveform_plot.add_layout(LinearAxis(axis_label="eTOF time bin"),
                             place="below")
    waveform_plot.add_layout(LinearAxis(axis_label="Intensity",
                                        major_label_orientation="vertical"),
                             place="left")

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

    # ---- multiline glyphs
    waveform_ref_source = ColumnDataSource(dict(xs=[], ys=[], en=[]))
    waveform_ref_multiline = waveform_plot.add_glyph(
        waveform_ref_source, MultiLine(xs="xs", ys="ys", line_color="blue"))

    waveform_str_source = ColumnDataSource(dict(xs=[], ys=[], en=[]))
    waveform_str_multiline = waveform_plot.add_glyph(
        waveform_str_source, MultiLine(xs="xs", ys="ys", line_color="red"))

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

    # ---- vertical spans
    photon_peak_ref_span = Span(location=0,
                                dimension="height",
                                line_dash="dashed",
                                line_color="blue")
    photon_peak_str_span = Span(location=0,
                                dimension="height",
                                line_dash="dashed",
                                line_color="red")
    waveform_plot.add_layout(photon_peak_ref_span)
    waveform_plot.add_layout(photon_peak_str_span)

    # Calibration fit plot
    fit_plot = Plot(
        title=Title(text="eTOF calibration fit"),
        x_range=DataRange1d(),
        y_range=DataRange1d(),
        plot_height=PLOT_CANVAS_HEIGHT,
        plot_width=PLOT_CANVAS_WIDTH,
        toolbar_location="right",
    )

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

    # ---- axes
    fit_plot.add_layout(LinearAxis(axis_label="Photoelectron peak shift"),
                        place="below")
    fit_plot.add_layout(LinearAxis(axis_label="Photon energy, eV",
                                   major_label_orientation="vertical"),
                        place="left")

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

    # ---- circle glyphs
    fit_ref_circle_source = ColumnDataSource(dict(x=[], y=[]))
    fit_ref_circle = fit_plot.add_glyph(
        fit_ref_circle_source, Circle(x="x", y="y", line_color="blue"))
    fit_str_circle_source = ColumnDataSource(dict(x=[], y=[]))
    fit_str_circle = fit_plot.add_glyph(fit_str_circle_source,
                                        Circle(x="x", y="y", line_color="red"))

    # ---- line glyphs
    fit_ref_line_source = ColumnDataSource(dict(x=[], y=[]))
    fit_ref_line = fit_plot.add_glyph(fit_ref_line_source,
                                      Line(x="x", y="y", line_color="blue"))
    fit_str_line_source = ColumnDataSource(dict(x=[], y=[]))
    fit_str_line = fit_plot.add_glyph(fit_str_line_source,
                                      Line(x="x", y="y", line_color="red"))

    # ---- legend
    fit_plot.add_layout(
        Legend(items=[
            ("reference", [fit_ref_circle, fit_ref_line]),
            ("streaked", [fit_str_circle, fit_str_line]),
        ]))
    fit_plot.legend.click_policy = "hide"

    # Calibration results datatables
    def datatable_ref_source_callback(_attr, _old_value, new_value):
        for en, ps, use in zip(new_value["energy"], new_value["peak_pos_ref"],
                               new_value["use_in_fit"]):
            palm.etofs["0"].calib_data.loc[
                en, "calib_tpeak"] = ps if ps != "NaN" else np.nan
            palm.etofs["0"].calib_data.loc[en, "use_in_fit"] = use

        calib_res = {}
        for etof_key in palm.etofs:
            calib_res[etof_key] = palm.etofs[etof_key].fit_calibration_curve()
        update_calibration_plot(calib_res)

    datatable_ref_source = ColumnDataSource(
        dict(energy=["", "", ""],
             peak_pos_ref=["", "", ""],
             use_in_fit=[True, True, True]))
    datatable_ref_source.on_change("data", datatable_ref_source_callback)

    datatable_ref = DataTable(
        source=datatable_ref_source,
        columns=[
            TableColumn(field="energy",
                        title="Photon Energy, eV",
                        editor=IntEditor()),
            TableColumn(field="peak_pos_ref",
                        title="Reference Peak",
                        editor=IntEditor()),
            TableColumn(field="use_in_fit",
                        title=" ",
                        editor=CheckboxEditor(),
                        width=80),
        ],
        index_position=None,
        editable=True,
        height=300,
        width=250,
    )

    def datatable_str_source_callback(_attr, _old_value, new_value):
        for en, ps, use in zip(new_value["energy"], new_value["peak_pos_str"],
                               new_value["use_in_fit"]):
            palm.etofs["1"].calib_data.loc[
                en, "calib_tpeak"] = ps if ps != "NaN" else np.nan
            palm.etofs["1"].calib_data.loc[en, "use_in_fit"] = use

        calib_res = {}
        for etof_key in palm.etofs:
            calib_res[etof_key] = palm.etofs[etof_key].fit_calibration_curve()
        update_calibration_plot(calib_res)

    datatable_str_source = ColumnDataSource(
        dict(energy=["", "", ""],
             peak_pos_str=["", "", ""],
             use_in_fit=[True, True, True]))
    datatable_str_source.on_change("data", datatable_str_source_callback)

    datatable_str = DataTable(
        source=datatable_str_source,
        columns=[
            TableColumn(field="energy",
                        title="Photon Energy, eV",
                        editor=IntEditor()),
            TableColumn(field="peak_pos_str",
                        title="Streaked Peak",
                        editor=IntEditor()),
            TableColumn(field="use_in_fit",
                        title=" ",
                        editor=CheckboxEditor(),
                        width=80),
        ],
        index_position=None,
        editable=True,
        height=350,
        width=250,
    )

    # eTOF calibration folder path text input
    def path_textinput_callback(_attr, _old_value, _new_value):
        path_periodic_update()
        update_load_dropdown_menu()

    path_textinput = TextInput(title="eTOF calibration path:",
                               value=os.path.join(os.path.expanduser("~")),
                               width=510)
    path_textinput.on_change("value", path_textinput_callback)

    # eTOF calibration eco scans dropdown
    def scans_dropdown_callback(_attr, _old_value, new_value):
        scans_dropdown.label = new_value

    scans_dropdown = Dropdown(label="ECO scans",
                              button_type="default",
                              menu=[])
    scans_dropdown.on_change("value", scans_dropdown_callback)

    # ---- etof scans periodic update
    def path_periodic_update():
        new_menu = []
        if os.path.isdir(path_textinput.value):
            for entry in os.scandir(path_textinput.value):
                if entry.is_file() and entry.name.endswith(".json"):
                    new_menu.append((entry.name, entry.name))
        scans_dropdown.menu = sorted(new_menu, reverse=True)

    doc.add_periodic_callback(path_periodic_update, 5000)

    # Calibrate button
    def calibrate_button_callback():
        try:
            palm.calibrate_etof_eco(eco_scan_filename=os.path.join(
                path_textinput.value, scans_dropdown.value))
        except Exception:
            palm.calibrate_etof(folder_name=path_textinput.value)

        datatable_ref_source.data.update(
            energy=palm.etofs["0"].calib_data.index.tolist(),
            peak_pos_ref=palm.etofs["0"].calib_data["calib_tpeak"].tolist(),
            use_in_fit=palm.etofs["0"].calib_data["use_in_fit"].tolist(),
        )

        datatable_str_source.data.update(
            energy=palm.etofs["0"].calib_data.index.tolist(),
            peak_pos_str=palm.etofs["1"].calib_data["calib_tpeak"].tolist(),
            use_in_fit=palm.etofs["1"].calib_data["use_in_fit"].tolist(),
        )

    def update_calibration_plot(calib_res):
        etof_ref = palm.etofs["0"]
        etof_str = palm.etofs["1"]

        shift_val = 0
        etof_ref_wf_shifted = []
        etof_str_wf_shifted = []
        for wf_ref, wf_str in zip(etof_ref.calib_data["waveform"],
                                  etof_str.calib_data["waveform"]):
            shift_val -= max(wf_ref.max(), wf_str.max())
            etof_ref_wf_shifted.append(wf_ref + shift_val)
            etof_str_wf_shifted.append(wf_str + shift_val)

        waveform_ref_source.data.update(
            xs=len(etof_ref.calib_data) *
            [list(range(etof_ref.internal_time_bins))],
            ys=etof_ref_wf_shifted,
            en=etof_ref.calib_data.index.tolist(),
        )

        waveform_str_source.data.update(
            xs=len(etof_str.calib_data) *
            [list(range(etof_str.internal_time_bins))],
            ys=etof_str_wf_shifted,
            en=etof_str.calib_data.index.tolist(),
        )

        photon_peak_ref_span.location = etof_ref.calib_t0
        photon_peak_str_span.location = etof_str.calib_t0

        def plot_fit(time, calib_a, calib_b):
            time_fit = np.linspace(np.nanmin(time), np.nanmax(time), 100)
            en_fit = (calib_a / time_fit)**2 + calib_b
            return time_fit, en_fit

        def update_plot(calib_results, circle, line):
            (a, c), x, y = calib_results
            x_fit, y_fit = plot_fit(x, a, c)
            circle.data.update(x=x, y=y)
            line.data.update(x=x_fit, y=y_fit)

        update_plot(calib_res["0"], fit_ref_circle_source, fit_ref_line_source)
        update_plot(calib_res["1"], fit_str_circle_source, fit_str_line_source)

        calib_const_div.text = f"""
        a_str = {etof_str.calib_a:.2f}<br>
        b_str = {etof_str.calib_b:.2f}<br>
        <br>
        a_ref = {etof_ref.calib_a:.2f}<br>
        b_ref = {etof_ref.calib_b:.2f}
        """

    calibrate_button = Button(label="Calibrate eTOF",
                              button_type="default",
                              width=250)
    calibrate_button.on_click(calibrate_button_callback)

    # Photon peak noise threshold value text input
    def phot_peak_noise_thr_spinner_callback(_attr, old_value, new_value):
        if new_value > 0:
            for etof in palm.etofs.values():
                etof.photon_peak_noise_thr = new_value
        else:
            phot_peak_noise_thr_spinner.value = old_value

    phot_peak_noise_thr_spinner = Spinner(title="Photon peak noise threshold:",
                                          value=1,
                                          step=0.1)
    phot_peak_noise_thr_spinner.on_change(
        "value", phot_peak_noise_thr_spinner_callback)

    # Electron peak noise threshold value text input
    def el_peak_noise_thr_spinner_callback(_attr, old_value, new_value):
        if new_value > 0:
            for etof in palm.etofs.values():
                etof.electron_peak_noise_thr = new_value
        else:
            el_peak_noise_thr_spinner.value = old_value

    el_peak_noise_thr_spinner = Spinner(title="Electron peak noise threshold:",
                                        value=10,
                                        step=0.1)
    el_peak_noise_thr_spinner.on_change("value",
                                        el_peak_noise_thr_spinner_callback)

    # Save calibration button
    def save_button_callback():
        palm.save_etof_calib(path=path_textinput.value)
        update_load_dropdown_menu()

    save_button = Button(label="Save", button_type="default", width=250)
    save_button.on_click(save_button_callback)

    # Load calibration button
    def load_dropdown_callback(_attr, _old_value, new_value):
        if new_value:
            palm.load_etof_calib(os.path.join(path_textinput.value, new_value))

            datatable_ref_source.data.update(
                energy=palm.etofs["0"].calib_data.index.tolist(),
                peak_pos_ref=palm.etofs["0"].calib_data["calib_tpeak"].tolist(
                ),
                use_in_fit=palm.etofs["0"].calib_data["use_in_fit"].tolist(),
            )

            datatable_str_source.data.update(
                energy=palm.etofs["0"].calib_data.index.tolist(),
                peak_pos_str=palm.etofs["1"].calib_data["calib_tpeak"].tolist(
                ),
                use_in_fit=palm.etofs["1"].calib_data["use_in_fit"].tolist(),
            )

            # Drop selection, so that this callback can be triggered again on the same dropdown menu
            # item from the user perspective
            load_dropdown.value = ""

    def update_load_dropdown_menu():
        new_menu = []
        calib_file_ext = ".palm_etof"
        if os.path.isdir(path_textinput.value):
            for entry in os.scandir(path_textinput.value):
                if entry.is_file() and entry.name.endswith((calib_file_ext)):
                    new_menu.append(
                        (entry.name[:-len(calib_file_ext)], entry.name))
            load_dropdown.button_type = "default"
            load_dropdown.menu = sorted(new_menu, reverse=True)
        else:
            load_dropdown.button_type = "danger"
            load_dropdown.menu = new_menu

    doc.add_next_tick_callback(update_load_dropdown_menu)
    doc.add_periodic_callback(update_load_dropdown_menu, 5000)

    load_dropdown = Dropdown(label="Load", menu=[], width=250)
    load_dropdown.on_change("value", load_dropdown_callback)

    # eTOF fitting equation
    fit_eq_div = Div(
        text="""Fitting equation:<br><br><img src="/palm/static/5euwuy.gif">"""
    )

    # Calibration constants
    calib_const_div = Div(text=f"""
        a_str = {0}<br>
        b_str = {0}<br>
        <br>
        a_ref = {0}<br>
        b_ref = {0}
        """)

    # assemble
    tab_layout = column(
        row(
            column(waveform_plot, fit_plot),
            Spacer(width=30),
            column(
                path_textinput,
                scans_dropdown,
                calibrate_button,
                phot_peak_noise_thr_spinner,
                el_peak_noise_thr_spinner,
                row(save_button, load_dropdown),
                row(datatable_ref, datatable_str),
                calib_const_div,
                fit_eq_div,
            ),
        ))

    return Panel(child=tab_layout, title="eTOF Calibration")
Пример #28
0
# initialize data source
source_function1 = ColumnDataSource(data=dict(x=[], y=[]))
source_function2 = ColumnDataSource(data=dict(x=[], y=[]))
source_result = ColumnDataSource(data=dict(x=[], y=[]))
source_convolution = ColumnDataSource(data=dict(x=[], y=[]))
source_xmarker = ColumnDataSource(data=dict(x=[], y=[]))
source_overlay = ColumnDataSource(data=dict(x=[], y=[], y_neg=[], y_pos=[]))

# initialize properties
update_is_enabled = True

# initialize controls
# dropdown menu for sample functions
function_type = Dropdown(
    label="choose a sample function pair or enter one below",
    menu=convolution_settings.sample_function_names)
function_type.on_click(function_pair_input_change)

# slider controlling the evaluated x value of the convolved function
x_value_input = Slider(title="x value",
                       name='x value',
                       value=convolution_settings.x_value_init,
                       start=convolution_settings.x_value_min,
                       end=convolution_settings.x_value_max,
                       step=convolution_settings.x_value_step)
x_value_input.on_change('value', input_change)
# text input for the first function to be convolved
function1_input = TextInput(value=convolution_settings.function1_input_init,
                            title="my first function:")
function1_input.on_change('value', input_change)
Пример #29
0
def create():
    det_data = []
    fit_params = {}
    js_data = ColumnDataSource(data=dict(content=["", ""], fname=["", ""]))

    def proposal_textinput_callback(_attr, _old, new):
        proposal = new.strip()
        for zebra_proposals_path in pyzebra.ZEBRA_PROPOSALS_PATHS:
            proposal_path = os.path.join(zebra_proposals_path, proposal)
            if os.path.isdir(proposal_path):
                # found it
                break
        else:
            raise ValueError(f"Can not find data for proposal '{proposal}'.")

        file_list = []
        for file in os.listdir(proposal_path):
            if file.endswith((".ccl", ".dat")):
                file_list.append((os.path.join(proposal_path, file), file))
        file_select.options = file_list
        file_open_button.disabled = False
        file_append_button.disabled = False

    proposal_textinput = TextInput(title="Proposal number:", width=210)
    proposal_textinput.on_change("value", proposal_textinput_callback)

    def _init_datatable():
        scan_list = [s["idx"] for s in det_data]
        file_list = []
        for scan in det_data:
            file_list.append(os.path.basename(scan["original_filename"]))

        scan_table_source.data.update(
            file=file_list,
            scan=scan_list,
            param=[None] * len(scan_list),
            fit=[0] * len(scan_list),
            export=[True] * len(scan_list),
        )
        scan_table_source.selected.indices = []
        scan_table_source.selected.indices = [0]

        scan_motor_select.options = det_data[0]["scan_motors"]
        scan_motor_select.value = det_data[0]["scan_motor"]
        param_select.value = "user defined"

    file_select = MultiSelect(title="Available .ccl/.dat files:",
                              width=210,
                              height=250)

    def file_open_button_callback():
        nonlocal det_data
        det_data = []
        for f_name in file_select.value:
            with open(f_name) as file:
                base, ext = os.path.splitext(f_name)
                if det_data:
                    append_data = pyzebra.parse_1D(file, ext)
                    pyzebra.normalize_dataset(append_data,
                                              monitor_spinner.value)
                    det_data.extend(append_data)
                else:
                    det_data = pyzebra.parse_1D(file, ext)
                    pyzebra.normalize_dataset(det_data, monitor_spinner.value)
                    js_data.data.update(
                        fname=[base + ".comm", base + ".incomm"])

        _init_datatable()
        append_upload_button.disabled = False

    file_open_button = Button(label="Open New", width=100, disabled=True)
    file_open_button.on_click(file_open_button_callback)

    def file_append_button_callback():
        for f_name in file_select.value:
            with open(f_name) as file:
                _, ext = os.path.splitext(f_name)
                append_data = pyzebra.parse_1D(file, ext)

            pyzebra.normalize_dataset(append_data, monitor_spinner.value)
            det_data.extend(append_data)

        _init_datatable()

    file_append_button = Button(label="Append", width=100, disabled=True)
    file_append_button.on_click(file_append_button_callback)

    def upload_button_callback(_attr, _old, new):
        nonlocal det_data
        det_data = []
        for f_str, f_name in zip(new, upload_button.filename):
            with io.StringIO(base64.b64decode(f_str).decode()) as file:
                base, ext = os.path.splitext(f_name)
                if det_data:
                    append_data = pyzebra.parse_1D(file, ext)
                    pyzebra.normalize_dataset(append_data,
                                              monitor_spinner.value)
                    det_data.extend(append_data)
                else:
                    det_data = pyzebra.parse_1D(file, ext)
                    pyzebra.normalize_dataset(det_data, monitor_spinner.value)
                    js_data.data.update(
                        fname=[base + ".comm", base + ".incomm"])

        _init_datatable()
        append_upload_button.disabled = False

    upload_div = Div(text="or upload new .ccl/.dat files:",
                     margin=(5, 5, 0, 5))
    upload_button = FileInput(accept=".ccl,.dat", multiple=True, width=200)
    upload_button.on_change("value", upload_button_callback)

    def append_upload_button_callback(_attr, _old, new):
        for f_str, f_name in zip(new, append_upload_button.filename):
            with io.StringIO(base64.b64decode(f_str).decode()) as file:
                _, ext = os.path.splitext(f_name)
                append_data = pyzebra.parse_1D(file, ext)

            pyzebra.normalize_dataset(append_data, monitor_spinner.value)
            det_data.extend(append_data)

        _init_datatable()

    append_upload_div = Div(text="append extra files:", margin=(5, 5, 0, 5))
    append_upload_button = FileInput(accept=".ccl,.dat",
                                     multiple=True,
                                     width=200,
                                     disabled=True)
    append_upload_button.on_change("value", append_upload_button_callback)

    def monitor_spinner_callback(_attr, _old, new):
        if det_data:
            pyzebra.normalize_dataset(det_data, new)
            _update_plot()

    monitor_spinner = Spinner(title="Monitor:",
                              mode="int",
                              value=100_000,
                              low=1,
                              width=145)
    monitor_spinner.on_change("value", monitor_spinner_callback)

    def scan_motor_select_callback(_attr, _old, new):
        if det_data:
            for scan in det_data:
                scan["scan_motor"] = new
            _update_plot()

    scan_motor_select = Select(title="Scan motor:", options=[], width=145)
    scan_motor_select.on_change("value", scan_motor_select_callback)

    def _update_table():
        fit_ok = [(1 if "fit" in scan else 0) for scan in det_data]
        scan_table_source.data.update(fit=fit_ok)

    def _update_plot():
        _update_single_scan_plot(_get_selected_scan())
        _update_overview()

    def _update_single_scan_plot(scan):
        scan_motor = scan["scan_motor"]

        y = scan["counts"]
        x = scan[scan_motor]

        plot.axis[0].axis_label = scan_motor
        plot_scatter_source.data.update(x=x,
                                        y=y,
                                        y_upper=y + np.sqrt(y),
                                        y_lower=y - np.sqrt(y))

        fit = scan.get("fit")
        if fit is not None:
            x_fit = np.linspace(x[0], x[-1], 100)
            plot_fit_source.data.update(x=x_fit, y=fit.eval(x=x_fit))

            x_bkg = []
            y_bkg = []
            xs_peak = []
            ys_peak = []
            comps = fit.eval_components(x=x_fit)
            for i, model in enumerate(fit_params):
                if "linear" in model:
                    x_bkg = x_fit
                    y_bkg = comps[f"f{i}_"]

                elif any(val in model
                         for val in ("gaussian", "voigt", "pvoigt")):
                    xs_peak.append(x_fit)
                    ys_peak.append(comps[f"f{i}_"])

            plot_bkg_source.data.update(x=x_bkg, y=y_bkg)
            plot_peak_source.data.update(xs=xs_peak, ys=ys_peak)

            fit_output_textinput.value = fit.fit_report()

        else:
            plot_fit_source.data.update(x=[], y=[])
            plot_bkg_source.data.update(x=[], y=[])
            plot_peak_source.data.update(xs=[], ys=[])
            fit_output_textinput.value = ""

    def _update_overview():
        xs = []
        ys = []
        param = []
        x = []
        y = []
        par = []
        for s, p in enumerate(scan_table_source.data["param"]):
            if p is not None:
                scan = det_data[s]
                scan_motor = scan["scan_motor"]
                xs.append(scan[scan_motor])
                x.extend(scan[scan_motor])
                ys.append(scan["counts"])
                y.extend([float(p)] * len(scan[scan_motor]))
                param.append(float(p))
                par.extend(scan["counts"])

        if det_data:
            scan_motor = det_data[0]["scan_motor"]
            ov_plot.axis[0].axis_label = scan_motor
            ov_param_plot.axis[0].axis_label = scan_motor

        ov_plot_mline_source.data.update(xs=xs,
                                         ys=ys,
                                         param=param,
                                         color=color_palette(len(xs)))

        if y:
            mapper["transform"].low = np.min([np.min(y) for y in ys])
            mapper["transform"].high = np.max([np.max(y) for y in ys])
        ov_param_plot_scatter_source.data.update(x=x, y=y, param=par)

        if y:
            interp_f = interpolate.interp2d(x, y, par)
            x1, x2 = min(x), max(x)
            y1, y2 = min(y), max(y)
            image = interp_f(
                np.linspace(x1, x2, ov_param_plot.inner_width // 10),
                np.linspace(y1, y2, ov_param_plot.inner_height // 10),
                assume_sorted=True,
            )
            ov_param_plot_image_source.data.update(image=[image],
                                                   x=[x1],
                                                   y=[y1],
                                                   dw=[x2 - x1],
                                                   dh=[y2 - y1])
        else:
            ov_param_plot_image_source.data.update(image=[],
                                                   x=[],
                                                   y=[],
                                                   dw=[],
                                                   dh=[])

    def _update_param_plot():
        x = []
        y = []
        fit_param = fit_param_select.value
        for s, p in zip(det_data, scan_table_source.data["param"]):
            if "fit" in s and fit_param:
                x.append(p)
                y.append(s["fit"].values[fit_param])

        param_plot_scatter_source.data.update(x=x, y=y)

    # Main plot
    plot = Plot(
        x_range=DataRange1d(),
        y_range=DataRange1d(only_visible=True),
        plot_height=450,
        plot_width=700,
    )

    plot.add_layout(LinearAxis(axis_label="Counts"), place="left")
    plot.add_layout(LinearAxis(axis_label="Scan motor"), place="below")

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

    plot_scatter_source = ColumnDataSource(
        dict(x=[0], y=[0], y_upper=[0], y_lower=[0]))
    plot_scatter = plot.add_glyph(
        plot_scatter_source, Scatter(x="x", y="y", line_color="steelblue"))
    plot.add_layout(
        Whisker(source=plot_scatter_source,
                base="x",
                upper="y_upper",
                lower="y_lower"))

    plot_fit_source = ColumnDataSource(dict(x=[0], y=[0]))
    plot_fit = plot.add_glyph(plot_fit_source, Line(x="x", y="y"))

    plot_bkg_source = ColumnDataSource(dict(x=[0], y=[0]))
    plot_bkg = plot.add_glyph(
        plot_bkg_source,
        Line(x="x", y="y", line_color="green", line_dash="dashed"))

    plot_peak_source = ColumnDataSource(dict(xs=[[0]], ys=[[0]]))
    plot_peak = plot.add_glyph(
        plot_peak_source,
        MultiLine(xs="xs", ys="ys", line_color="red", line_dash="dashed"))

    fit_from_span = Span(location=None, dimension="height", line_dash="dashed")
    plot.add_layout(fit_from_span)

    fit_to_span = Span(location=None, dimension="height", line_dash="dashed")
    plot.add_layout(fit_to_span)

    plot.add_layout(
        Legend(
            items=[
                ("data", [plot_scatter]),
                ("best fit", [plot_fit]),
                ("peak", [plot_peak]),
                ("linear", [plot_bkg]),
            ],
            location="top_left",
            click_policy="hide",
        ))

    plot.add_tools(PanTool(), WheelZoomTool(), ResetTool())
    plot.toolbar.logo = None

    # Overview multilines plot
    ov_plot = Plot(x_range=DataRange1d(),
                   y_range=DataRange1d(),
                   plot_height=450,
                   plot_width=700)

    ov_plot.add_layout(LinearAxis(axis_label="Counts"), place="left")
    ov_plot.add_layout(LinearAxis(axis_label="Scan motor"), place="below")

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

    ov_plot_mline_source = ColumnDataSource(
        dict(xs=[], ys=[], param=[], color=[]))
    ov_plot.add_glyph(ov_plot_mline_source,
                      MultiLine(xs="xs", ys="ys", line_color="color"))

    hover_tool = HoverTool(tooltips=[("param", "@param")])
    ov_plot.add_tools(PanTool(), WheelZoomTool(), hover_tool, ResetTool())

    ov_plot.add_tools(PanTool(), WheelZoomTool(), ResetTool())
    ov_plot.toolbar.logo = None

    # Overview perams plot
    ov_param_plot = Plot(x_range=DataRange1d(),
                         y_range=DataRange1d(),
                         plot_height=450,
                         plot_width=700)

    ov_param_plot.add_layout(LinearAxis(axis_label="Param"), place="left")
    ov_param_plot.add_layout(LinearAxis(axis_label="Scan motor"),
                             place="below")

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

    ov_param_plot_image_source = ColumnDataSource(
        dict(image=[], x=[], y=[], dw=[], dh=[]))
    ov_param_plot.add_glyph(
        ov_param_plot_image_source,
        Image(image="image", x="x", y="y", dw="dw", dh="dh"))

    ov_param_plot_scatter_source = ColumnDataSource(dict(x=[], y=[], param=[]))
    mapper = linear_cmap(field_name="param", palette=Turbo256, low=0, high=50)
    ov_param_plot.add_glyph(
        ov_param_plot_scatter_source,
        Scatter(x="x", y="y", line_color=mapper, fill_color=mapper, size=10),
    )

    ov_param_plot.add_tools(PanTool(), WheelZoomTool(), ResetTool())
    ov_param_plot.toolbar.logo = None

    # Parameter plot
    param_plot = Plot(x_range=DataRange1d(),
                      y_range=DataRange1d(),
                      plot_height=400,
                      plot_width=700)

    param_plot.add_layout(LinearAxis(axis_label="Fit parameter"), place="left")
    param_plot.add_layout(LinearAxis(axis_label="Parameter"), place="below")

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

    param_plot_scatter_source = ColumnDataSource(dict(x=[], y=[]))
    param_plot.add_glyph(param_plot_scatter_source, Scatter(x="x", y="y"))

    param_plot.add_tools(PanTool(), WheelZoomTool(), ResetTool())
    param_plot.toolbar.logo = None

    def fit_param_select_callback(_attr, _old, _new):
        _update_param_plot()

    fit_param_select = Select(title="Fit parameter", options=[], width=145)
    fit_param_select.on_change("value", fit_param_select_callback)

    # Plot tabs
    plots = Tabs(tabs=[
        Panel(child=plot, title="single scan"),
        Panel(child=ov_plot, title="overview"),
        Panel(child=ov_param_plot, title="overview map"),
        Panel(child=column(param_plot, row(fit_param_select)),
              title="parameter plot"),
    ])

    # Scan select
    def scan_table_select_callback(_attr, old, new):
        if not new:
            # skip empty selections
            return

        # Avoid selection of multiple indicies (via Shift+Click or Ctrl+Click)
        if len(new) > 1:
            # drop selection to the previous one
            scan_table_source.selected.indices = old
            return

        if len(old) > 1:
            # skip unnecessary update caused by selection drop
            return

        _update_plot()

    def scan_table_source_callback(_attr, _old, _new):
        _update_preview()

    scan_table_source = ColumnDataSource(
        dict(file=[], scan=[], param=[], fit=[], export=[]))
    scan_table_source.on_change("data", scan_table_source_callback)

    scan_table = DataTable(
        source=scan_table_source,
        columns=[
            TableColumn(field="file", title="file", width=150),
            TableColumn(field="scan", title="scan", width=50),
            TableColumn(field="param",
                        title="param",
                        editor=NumberEditor(),
                        width=50),
            TableColumn(field="fit", title="Fit", width=50),
            TableColumn(field="export",
                        title="Export",
                        editor=CheckboxEditor(),
                        width=50),
        ],
        width=410,  # +60 because of the index column
        editable=True,
        autosize_mode="none",
    )

    def scan_table_source_callback(_attr, _old, _new):
        if scan_table_source.selected.indices:
            _update_plot()

    scan_table_source.selected.on_change("indices", scan_table_select_callback)
    scan_table_source.on_change("data", scan_table_source_callback)

    def _get_selected_scan():
        return det_data[scan_table_source.selected.indices[0]]

    def param_select_callback(_attr, _old, new):
        if new == "user defined":
            param = [None] * len(det_data)
        else:
            param = [scan[new] for scan in det_data]

        scan_table_source.data["param"] = param
        _update_param_plot()

    param_select = Select(
        title="Parameter:",
        options=["user defined", "temp", "mf", "h", "k", "l"],
        value="user defined",
        width=145,
    )
    param_select.on_change("value", param_select_callback)

    def fit_from_spinner_callback(_attr, _old, new):
        fit_from_span.location = new

    fit_from_spinner = Spinner(title="Fit from:", width=145)
    fit_from_spinner.on_change("value", fit_from_spinner_callback)

    def fit_to_spinner_callback(_attr, _old, new):
        fit_to_span.location = new

    fit_to_spinner = Spinner(title="to:", width=145)
    fit_to_spinner.on_change("value", fit_to_spinner_callback)

    def fitparams_add_dropdown_callback(click):
        # bokeh requires (str, str) for MultiSelect options
        new_tag = f"{click.item}-{fitparams_select.tags[0]}"
        fitparams_select.options.append((new_tag, click.item))
        fit_params[new_tag] = fitparams_factory(click.item)
        fitparams_select.tags[0] += 1

    fitparams_add_dropdown = Dropdown(
        label="Add fit function",
        menu=[
            ("Linear", "linear"),
            ("Gaussian", "gaussian"),
            ("Voigt", "voigt"),
            ("Pseudo Voigt", "pvoigt"),
            # ("Pseudo Voigt1", "pseudovoigt1"),
        ],
        width=145,
    )
    fitparams_add_dropdown.on_click(fitparams_add_dropdown_callback)

    def fitparams_select_callback(_attr, old, new):
        # Avoid selection of multiple indicies (via Shift+Click or Ctrl+Click)
        if len(new) > 1:
            # drop selection to the previous one
            fitparams_select.value = old
            return

        if len(old) > 1:
            # skip unnecessary update caused by selection drop
            return

        if new:
            fitparams_table_source.data.update(fit_params[new[0]])
        else:
            fitparams_table_source.data.update(
                dict(param=[], value=[], vary=[], min=[], max=[]))

    fitparams_select = MultiSelect(options=[], height=120, width=145)
    fitparams_select.tags = [0]
    fitparams_select.on_change("value", fitparams_select_callback)

    def fitparams_remove_button_callback():
        if fitparams_select.value:
            sel_tag = fitparams_select.value[0]
            del fit_params[sel_tag]
            for elem in fitparams_select.options:
                if elem[0] == sel_tag:
                    fitparams_select.options.remove(elem)
                    break

            fitparams_select.value = []

    fitparams_remove_button = Button(label="Remove fit function", width=145)
    fitparams_remove_button.on_click(fitparams_remove_button_callback)

    def fitparams_factory(function):
        if function == "linear":
            params = ["slope", "intercept"]
        elif function == "gaussian":
            params = ["amplitude", "center", "sigma"]
        elif function == "voigt":
            params = ["amplitude", "center", "sigma", "gamma"]
        elif function == "pvoigt":
            params = ["amplitude", "center", "sigma", "fraction"]
        elif function == "pseudovoigt1":
            params = ["amplitude", "center", "g_sigma", "l_sigma", "fraction"]
        else:
            raise ValueError("Unknown fit function")

        n = len(params)
        fitparams = dict(
            param=params,
            value=[None] * n,
            vary=[True] * n,
            min=[None] * n,
            max=[None] * n,
        )

        if function == "linear":
            fitparams["value"] = [0, 1]
            fitparams["vary"] = [False, True]
            fitparams["min"] = [None, 0]

        elif function == "gaussian":
            fitparams["min"] = [0, None, None]

        return fitparams

    fitparams_table_source = ColumnDataSource(
        dict(param=[], value=[], vary=[], min=[], max=[]))
    fitparams_table = DataTable(
        source=fitparams_table_source,
        columns=[
            TableColumn(field="param", title="Parameter"),
            TableColumn(field="value", title="Value", editor=NumberEditor()),
            TableColumn(field="vary", title="Vary", editor=CheckboxEditor()),
            TableColumn(field="min", title="Min", editor=NumberEditor()),
            TableColumn(field="max", title="Max", editor=NumberEditor()),
        ],
        height=200,
        width=350,
        index_position=None,
        editable=True,
        auto_edit=True,
    )

    # start with `background` and `gauss` fit functions added
    fitparams_add_dropdown_callback(types.SimpleNamespace(item="linear"))
    fitparams_add_dropdown_callback(types.SimpleNamespace(item="gaussian"))
    fitparams_select.value = ["gaussian-1"]  # add selection to gauss

    fit_output_textinput = TextAreaInput(title="Fit results:",
                                         width=750,
                                         height=200)

    def proc_all_button_callback():
        for scan, export in zip(det_data, scan_table_source.data["export"]):
            if export:
                pyzebra.fit_scan(scan,
                                 fit_params,
                                 fit_from=fit_from_spinner.value,
                                 fit_to=fit_to_spinner.value)
                pyzebra.get_area(
                    scan,
                    area_method=AREA_METHODS[area_method_radiobutton.active],
                    lorentz=lorentz_checkbox.active,
                )

        _update_plot()
        _update_table()

        for scan in det_data:
            if "fit" in scan:
                options = list(scan["fit"].params.keys())
                fit_param_select.options = options
                fit_param_select.value = options[0]
                break
        _update_param_plot()

    proc_all_button = Button(label="Process All",
                             button_type="primary",
                             width=145)
    proc_all_button.on_click(proc_all_button_callback)

    def proc_button_callback():
        scan = _get_selected_scan()
        pyzebra.fit_scan(scan,
                         fit_params,
                         fit_from=fit_from_spinner.value,
                         fit_to=fit_to_spinner.value)
        pyzebra.get_area(
            scan,
            area_method=AREA_METHODS[area_method_radiobutton.active],
            lorentz=lorentz_checkbox.active,
        )

        _update_plot()
        _update_table()

        for scan in det_data:
            if "fit" in scan:
                options = list(scan["fit"].params.keys())
                fit_param_select.options = options
                fit_param_select.value = options[0]
                break
        _update_param_plot()

    proc_button = Button(label="Process Current", width=145)
    proc_button.on_click(proc_button_callback)

    area_method_div = Div(text="Intensity:", margin=(5, 5, 0, 5))
    area_method_radiobutton = RadioGroup(labels=["Function", "Area"],
                                         active=0,
                                         width=145)

    lorentz_checkbox = CheckboxGroup(labels=["Lorentz Correction"],
                                     width=145,
                                     margin=(13, 5, 5, 5))

    export_preview_textinput = TextAreaInput(title="Export file preview:",
                                             width=450,
                                             height=400)

    def _update_preview():
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_file = temp_dir + "/temp"
            export_data = []
            for s, export in zip(det_data, scan_table_source.data["export"]):
                if export:
                    export_data.append(s)

            # pyzebra.export_1D(export_data, temp_file, "fullprof")

            exported_content = ""
            file_content = []
            for ext in (".comm", ".incomm"):
                fname = temp_file + ext
                if os.path.isfile(fname):
                    with open(fname) as f:
                        content = f.read()
                        exported_content += f"{ext} file:\n" + content
                else:
                    content = ""
                file_content.append(content)

            js_data.data.update(content=file_content)
            export_preview_textinput.value = exported_content

    save_button = Button(label="Download File(s)",
                         button_type="success",
                         width=220)
    save_button.js_on_click(
        CustomJS(args={"js_data": js_data}, code=javaScript))

    fitpeak_controls = row(
        column(fitparams_add_dropdown, fitparams_select,
               fitparams_remove_button),
        fitparams_table,
        Spacer(width=20),
        column(fit_from_spinner, lorentz_checkbox, area_method_div,
               area_method_radiobutton),
        column(fit_to_spinner, proc_button, proc_all_button),
    )

    scan_layout = column(scan_table,
                         row(monitor_spinner, scan_motor_select, param_select))

    import_layout = column(
        proposal_textinput,
        file_select,
        row(file_open_button, file_append_button),
        upload_div,
        upload_button,
        append_upload_div,
        append_upload_button,
    )

    export_layout = column(export_preview_textinput, row(save_button))

    tab_layout = column(
        row(import_layout, scan_layout, plots, Spacer(width=30),
            export_layout),
        row(fitpeak_controls, fit_output_textinput),
    )

    return Panel(child=tab_layout, title="param study")
Пример #30
0
button = Button(label="Button (enabled) - has click event", button_type="primary")
button.js_on_event("button_click", CustomJS(code="console.log('button: click ', this.toString())"))
button.js_on_event("button_click", CustomJS(code="console.log('button: click ', this.toString())"))

button_disabled = Button(label="Button (disabled) - no click event", button_type="primary", disabled=True)
button_disabled.js_on_event("button_click", CustomJS(code="console.log('button(disabled): click ', this.toString())"))

toggle_inactive = Toggle(label="Toggle button (initially inactive)", button_type="success")
toggle_inactive.js_on_event('button_click', CustomJS(code="console.log('toggle(inactive): active=' + this.origin.active, this.toString())"))

toggle_active = Toggle(label="Toggle button (initially active)", button_type="success", active=True)
toggle_active.js_on_event('button_click', CustomJS(code="console.log('toggle(active): active=' + this.origin.active, this.toString())"))

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

dropdown = Dropdown(label="Dropdown button", button_type="warning", menu=menu)
dropdown.js_on_event("button_click", CustomJS(code="console.log('dropdown: click ' + this.toString())"))
dropdown.js_on_event("menu_item_click", CustomJS(code="console.log('dropdown: ' + this.item, this.toString())"))

dropdown_disabled = Dropdown(label="Dropdown button (disabled)", button_type="warning", disabled=True, menu=menu)
dropdown_disabled.js_on_event("button_click", CustomJS(code="console.log('dropdown(disabled): click ' + this.toString())"))
dropdown_disabled.js_on_event("menu_item_click", CustomJS(code="console.log('dropdown(disabled): ' + this.item, this.toString())"))

dropdown_split = Dropdown(label="Split button", split=True, button_type="danger", menu=menu)
dropdown_split.js_on_event("button_click", CustomJS(code="console.log('dropdown(split): click ' + this.toString())"))
dropdown_split.js_on_event("menu_item_click", CustomJS(code="console.log('dropdown(split): ' + this.item, this.toString())"))

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

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