def modify_doc(doc):
     source = ColumnDataSource(dict(x=[1, 2], y=[1, 1], val=["a", "b"]))
     plot = Plot(plot_height=400, plot_width=400, x_range=Range1d(0, 1), y_range=Range1d(0, 1), min_border=0)
     plot.add_glyph(source, Circle(x='x', y='y', size=20))
     plot.add_tools(CustomAction(callback=CustomJS(args=dict(s=source), code=RECORD("data", "s.data"))))
     group = CheckboxButtonGroup(labels=LABELS, css_classes=["foo"])
     def cb(active):
         source.data['val'] = (active + [0, 0])[:2] # keep col length at 2, padded with zero
     group.on_click(cb)
     doc.add_root(column(group, plot))
 def modify_doc(doc):
     source = ColumnDataSource(dict(x=[1, 2], y=[1, 1], val=["a", "b"]))
     plot = Plot(plot_height=400, plot_width=400, x_range=Range1d(0, 1), y_range=Range1d(0, 1), min_border=0)
     plot.add_glyph(source, Circle(x='x', y='y', size=20))
     plot.add_tools(CustomAction(callback=CustomJS(args=dict(s=source), code=RECORD("data", "s.data"))))
     group = CheckboxButtonGroup(labels=LABELS, css_classes=["foo"])
     def cb(active):
         source.data['val'] = (active + [0, 0])[:2] # keep col length at 2, padded with zero
     group.on_click(cb)
     doc.add_root(column(group, plot))
示例#3
0
class DriftWidget:
    "Allows removing the drifts"
    __widget: CheckboxButtonGroup

    def __init__(self, ctrl, tasks: CyclesModelAccess) -> None:
        self.__theme = ctrl.theme.add(DriftWidgetTheme())
        self.__tasks = tasks

    def addtodoc(self, mainview, ctrl) -> List[Widget]:
        "creates the widget"
        self.__widget = CheckboxButtonGroup(labels=self.__theme.labels,
                                            name='Cycles:DriftWidget',
                                            width=self.__theme.width,
                                            height=self.__theme.height,
                                            css_classes=[self.__theme.title],
                                            **self.__data())
        self.__widget.on_click(mainview.actionifactive(ctrl)(self._onclick_cb))

        return [self.__widget]

    def _onclick_cb(self, value):
        "action to be performed when buttons are clicked"
        for ind, name in enumerate(('driftperbead', 'driftpercycle')):
            attr = getattr(self.__tasks, name)
            task = attr.task
            if (ind not in value) != (task is None):
                getattr(self.__tasks, name).update(disabled=ind not in value)

    def reset(self, resets: CACHE_TYPE):
        "updates the widget"
        resets[self.__widget].update(**self.__data())

    def __data(self) -> dict:
        value: List[int] = []
        if self.__tasks.driftperbead.task is not None:
            value = [0]
        if self.__tasks.driftpercycle.task is not None:
            value += [1]
        return dict(active=value)
def add_item():
    item_price_input = TextInput(value="[Preis]", width=140)
    item_name_input = TextInput(value="[Produkt]", width=140)
    toggle_as_coupon_button = CheckboxButtonGroup(labels=['G'],
                                                  active=[],
                                                  width_policy="min")
    remove_item_button = Button(label='X',
                                width_policy='min',
                                button_type='warning')
    this_layout = row(item_price_input, item_name_input,
                      toggle_as_coupon_button, remove_item_button)

    def remove_this_item(*args):
        item_data_column_layout.children.remove(this_layout)
        if len(item_data_column_layout.children) == 0:
            add_item()  #avoid empty layout
        else:
            update_coupon_and_endsum()

    remove_item_button.on_click(remove_this_item)
    toggle_as_coupon_button.on_click(update_coupon_and_endsum)
    item_price_input.on_change('value', update_coupon_and_endsum_wrapper)
    item_data_column_layout.children.append(this_layout)
    update_coupon_and_endsum()
示例#5
0
文件: main.py 项目: spmcelrath/demos
def check_btn_callback(attr):
    if 1 in attr:
        print("SYMBOLS ON")
        labels.visible = True
    else:
        print("SYMBOLS OFF")
        labels.visible = False
    if 0 in attr:
        print("LEGEND ON")
        plot.legend.visible = True
    else:
        print("LEGEND OFF")
        plot.legend.visible = False


checkbox_button_group.on_click(check_btn_callback)

date_picker = DatePicker(value="2019-01-22",
                         min_date="2019-01-22",
                         max_date="2020-11-22")

as_slider = Slider(start=1, end=10, value=5, step=.1, title="animation speed")
l_slider = RangeSlider(start=-1.00,
                       end=1.00,
                       value=(0.75, 1.0),
                       step=0.01,
                       title="show links for")
spacer = Spacer(width=240, height=700)
controls = [
    spacer, date_picker, af_button, ab_button, as_slider, l_slider,
    checkbox_button_group
示例#6
0
文件: base.py 项目: phurwicz/hover
class BokehBaseExplorer(Loggable, ABC):
    """
    ???+ note "Base class for visually exploring data with `Bokeh`."
        Assumes:

        - in supplied dataframes
          - (always) xy coordinates in `x` and `y` columns;
          - (always) an index for the rows;
          - (always) classification label (or ABSTAIN) in a `label` column.

        Does not assume:

        - a specific form of data;
        - what the map serves to do.
    """

    SUBSET_GLYPH_KWARGS = {}

    MANDATORY_COLUMNS = ["label", "x", "y"]
    TOOLTIP_KWARGS = {"label": True, "coords": True, "index": True}

    def __init__(self, df_dict, **kwargs):
        """
        ???+ note "Constructor shared by all child classes."
            | Param       | Type   | Description                  |
            | :---------- | :----- | :--------------------------- |
            | `df_dict`   | `dict` | `str` -> `DataFrame` mapping |
            | `**kwargs`  |        | forwarded to `bokeh.plotting.figure` |

            1. settle the figure settings by using child class defaults & kwargs overrides
            2. settle the glyph settings by using child class defaults
            3. create widgets that child classes can override
            4. create data sources the correspond to class-specific data subsets.
            5. activate builtin search callbacks depending on the child class.
            6. initialize a figure under the settings above
        """
        self.figure_kwargs = {
            "tools": STANDARD_PLOT_TOOLS,
            "tooltips": self._build_tooltip(kwargs.pop("tooltips", "")),
            # bokeh recommends webgl for scalability
            "output_backend": "webgl",
        }
        self.figure_kwargs.update(kwargs)
        self.figure = figure(**self.figure_kwargs)
        self.glyph_kwargs = {
            _key: _dict["constant"].copy()
            for _key, _dict in self.__class__.SUBSET_GLYPH_KWARGS.items()
        }
        self._setup_dfs(df_dict)
        self._setup_sources()
        self._setup_widgets()
        self._activate_search_builtin()

    @classmethod
    def from_dataset(cls, dataset, subset_mapping, *args, **kwargs):
        """
        ???+ note "Alternative constructor from a `SupervisableDataset`."
            | Param            | Type   | Description                  |
            | :--------------- | :----- | :--------------------------- |
            | `dataset`        | `SupervisableDataset` | dataset with `DataFrame`s |
            | `subset_mapping` | `dict` | `dataset` -> `explorer` subset mapping |
            | `*args`          |        | forwarded to the constructor |
            | `**kwargs`       |        | forwarded to the constructor |
        """
        # local import to avoid import cycles
        from hover.core.dataset import SupervisableDataset

        assert isinstance(dataset, SupervisableDataset)
        df_dict = {_v: dataset.dfs[_k] for _k, _v in subset_mapping.items()}
        return cls(df_dict, *args, **kwargs)

    def view(self):
        """
        ???+ note "Define the high-level visual layout of the whole explorer."
        """
        from bokeh.layouts import column

        return column(self._layout_widgets(), self.figure)

    def _build_tooltip(self, extra):
        """
        ???+ note "Define a windowed tooltip which shows inspection details."
            | Param            | Type   | Description                  |
            | :--------------- | :----- | :--------------------------- |
            | `extra`          | `str`  | user-supplied extra HTML |

            Note that this is a method rather than a class attribute because
            child classes may involve instance attributes in the tooltip.
        """
        standard = bokeh_hover_tooltip(**self.__class__.TOOLTIP_KWARGS)
        return f"{standard}\n{extra}"

    def _setup_widgets(self):
        """
        ???+ note "High-level function creating widgets for interactive functionality."
        """
        self._info("Setting up widgets")
        self._dynamic_widgets = OrderedDict()
        self._dynamic_callbacks = OrderedDict()
        self._setup_search_highlight()
        self._setup_selection_option()
        self._setup_subset_toggle()

    @abstractmethod
    def _layout_widgets(self):
        """
        ???+ note "Define the low-level layout of widgets."

        """
        pass

    @abstractmethod
    def _setup_search_highlight(self):
        """
        ???+ note "Define how to search and highlight data points."
            Left to child classes that have a specific feature format.
        """
        pass

    def _setup_selection_option(self):
        """
        ???+ note "Create a group of checkbox(es) for advanced selection options."
        """
        from bokeh.models import CheckboxGroup

        self.selection_option_box = CheckboxGroup(
            labels=["cumulative selection"], active=[]
        )

    def _setup_subset_toggle(self):
        """
        ???+ note "Create a group of buttons for toggling which data subsets to show."
        """
        from bokeh.models import CheckboxButtonGroup

        data_keys = list(self.__class__.SUBSET_GLYPH_KWARGS.keys())
        self.data_key_button_group = CheckboxButtonGroup(
            labels=data_keys, active=list(range(len(data_keys)))
        )

        def update_data_key_display(active):
            visible_keys = {self.data_key_button_group.labels[idx] for idx in active}
            for _renderer in self.figure.renderers:
                # if the renderer has a name "on the list", update its visibility
                if _renderer.name in self.__class__.SUBSET_GLYPH_KWARGS.keys():
                    _renderer.visible = _renderer.name in visible_keys

        # store the callback (useful, for example, during automated tests) and link it
        self._callback_subset_display = lambda: update_data_key_display(
            self.data_key_button_group.active
        )
        self.data_key_button_group.on_click(update_data_key_display)

    def value_patch(self, col_original, col_patch, **kwargs):
        """
        ???+ note "Allow source values to be dynamically patched through a slider."
            | Param            | Type   | Description                  |
            | :--------------- | :----- | :--------------------------- |
            | `col_original`   | `str`  | column of values before the patch |
            | `col_patch`      | `str`  | column of list of values to use as patches |
            | `**kwargs`       |        | forwarded to the slider |

            [Reference](https://github.com/bokeh/bokeh/blob/2.3.0/examples/howto/patch_app.py)
        """
        # add a patch slider to widgets, if none exist
        if "patch_slider" not in self._dynamic_widgets:
            slider = Slider(start=0, end=1, value=0, step=1, **kwargs)
            slider.disabled = True
            self._dynamic_widgets["patch_slider"] = slider
        else:
            slider = self._dynamic_widgets["patch_slider"]

        # create a slider-adjusting callback exposed to the outside
        def adjust_slider():
            """
            Infer slider length from the number of patch values.
            """
            num_patches = None
            for _key, _df in self.dfs.items():
                assert (
                    col_patch in _df.columns
                ), f"Subset {_key} expecting column {col_patch} among columns, got {_df.columns}"
                # find all array lengths; note that the data subset can be empty
                _num_patches_seen = _df[col_patch].apply(len).values
                assert (
                    len(set(_num_patches_seen)) <= 1
                ), f"Expecting consistent number of patches, got {_num_patches_seen}"
                _num_patches = _num_patches_seen[0] if _df.shape[0] > 0 else None

                # if a previous subset has implied the number of patches, run a consistency check
                if num_patches is None:
                    num_patches = _num_patches
                else:
                    assert (
                        num_patches == _num_patches
                    ), f"Conflicting number of patches: {num_patches} vs {_num_patches}"

            assert num_patches >= 2, f"Expecting at least 2 patches, got {num_patches}"
            slider.end = num_patches - 1
            slider.disabled = False

        self._dynamic_callbacks["adjust_patch_slider"] = adjust_slider

        # create the callback for patching values
        def update_patch(attr, old, new):
            for _key, _df in self.dfs.items():
                # calculate the patch corresponding to slider value
                _value = [_arr[new] for _arr in _df[col_patch].values]
                _slice = slice(_df.shape[0])
                _patch = {col_original: [(_slice, _value)]}
                self.sources[_key].patch(_patch)

        slider.on_change("value", update_patch)
        self._good(f"Patching {col_original} using {col_patch}")

    def _setup_dfs(self, df_dict, copy=False):
        """
        ???+ note "Check and store DataFrames **by reference by default**."
            Intended to be extended in child classes for pre/post processing.

            | Param       | Type   | Description                  |
            | :---------- | :----- | :--------------------------- |
            | `df_dict`   | `dict` | `str` -> `DataFrame` mapping |
            | `copy`      | `bool` | whether to copy `DataFrame`s |
        """
        self._info("Setting up DataFrames")
        supplied_keys = set(df_dict.keys())
        expected_keys = set(self.__class__.SUBSET_GLYPH_KWARGS.keys())

        # perform high-level df key checks
        supplied_not_expected = supplied_keys.difference(expected_keys)
        expected_not_supplied = expected_keys.difference(supplied_keys)

        for _key in supplied_not_expected:
            self._warn(
                f"{self.__class__.__name__}.__init__(): got unexpected df key {_key}"
            )
        for _key in expected_not_supplied:
            self._warn(
                f"{self.__class__.__name__}.__init__(): missing expected df key {_key}"
            )

        # create df with column checks
        self.dfs = dict()
        for _key, _df in df_dict.items():
            if _key in expected_keys:
                for _col in self.__class__.MANDATORY_COLUMNS:
                    if _col not in _df.columns:
                        # edge case: DataFrame has zero rows
                        assert (
                            _df.shape[0] == 0
                        ), f"Missing column '{_col}' from non-empty {_key} DataFrame: found {list(_df.columns)}"
                        _df[_col] = None

                self.dfs[_key] = _df.copy() if copy else _df

    def _setup_sources(self):
        """
        ???+ note "Create, **(not update)**, `ColumnDataSource` objects."
            Intended to be extended in child classes for pre/post processing.
        """
        self._info("Setting up sources")
        self.sources = {_key: ColumnDataSource(_df) for _key, _df in self.dfs.items()}
        self._postprocess_sources()

        # initialize attributes that couple with sources
        # extra columns for dynamic plotting
        self._extra_source_cols = defaultdict(dict)

        self._setup_selection_tools()

    def _setup_selection_tools(self):
        """
        ???+ note "Create data structures and callbacks for dynamic selections."
            Useful for linking and filtering selections across explorers.
        """
        from bokeh.events import SelectionGeometry

        # store the last manual selections
        self._last_selections = {
            _key: RootUnionFind(set()) for _key in self.sources.keys()
        }
        # store commutative, idempotent index filters
        self._selection_filters = {
            _key: RootUnionFind(set()) for _key in self.sources.keys()
        }

        def cumulative_selection_flag():
            """
            Determine whether cumulative selection is enabled.
            """
            return bool(0 in self.selection_option_box.active)

        def store_selection():
            """
            Keep track of the last manual selection.
            Useful for applying cumulation / filters dynamically.
            """
            # store selection indices
            for _key, _source in self.sources.items():
                _selected = _source.selected.indices
                # use clear() and update() instead of assignment to keep clean references
                if not cumulative_selection_flag():
                    self._last_selections[_key].data.clear()
                    self._last_selections[_key].data.update(_selected)
                else:
                    self._last_selections[_key].data.update(_selected)
                    _source.selected.indices = list(self._last_selections[_key].data)

        def trigger_selection_filters(subsets=None):
            """
            Filter selection indices on specified subsets.
            """
            if subsets is None:
                subsets = self.sources.keys()
            else:
                assert set(subsets).issubset(
                    self.sources.keys()
                ), f"Expected subsets from {self.sources.keys()}"

            for _key in subsets:
                _selected = self._last_selections[_key].data
                for _func in self._selection_filters[_key].data:
                    _selected = _func(_selected, _key)
                self.sources[_key].selected.indices = list(_selected)

        # keep reference to trigger_store_selection() for testing only
        self._store_selection = store_selection
        self.figure.on_event(
            SelectionGeometry,
            lambda event: self._store_selection() if event.final else None,
        )

        # keep reference to trigger_selection_filter() for further access
        # for example, toggling filters should call the trigger
        self._trigger_selection_filters = trigger_selection_filters
        self.figure.on_event(
            SelectionGeometry,
            lambda event: self._trigger_selection_filters() if event.final else None,
        )

    def _update_sources(self):
        """
        ???+ note "Update the sources with the corresponding dfs."
            Note that the shapes and fields of sources are overriden.
            Thus supplementary fields (those that do not exist in the dfs),
            such as dynamic plotting kwargs, need to be re-assigned.
        """
        for _key in self.dfs.keys():
            self.sources[_key].data = self.dfs[_key]
        self._postprocess_sources()
        # self._activate_search_builtin(verbose=False)

        # reset attribute values that couple with sources
        for _key in self.sources.keys():
            _num_points = len(self.sources[_key].data["label"])
            # add extra columns
            for _col, _fill_value in self._extra_source_cols[_key].items():
                self.sources[_key].add([_fill_value] * _num_points, _col)

            # clear last selection but keep the set object
            self._last_selections[_key].data.clear()
            # DON'T DO: self._last_selections = {_key: set() for _key in self.sources.keys()}

    def _postprocess_sources(self):
        """
        ???+ note "Infer source attributes from the dfs, without altering the dfs."
            Useful for assigning dynamic glyph attributes, similarly to `activate_search()`.
        """
        pass

    def _activate_search_builtin(self, verbose=True):
        """
        ???+ note "Assign Highlighting callbacks to search results in a manner built into the class."
            Typically called once during initialization.

            Note that this is a template method which heavily depends on class attributes.
            | Param       | Type   | Description                  |
            | :---------- | :----- | :--------------------------- |
            | `verbose`   | `bool` | whether to log verbosely     |
        """
        for _key, _dict in self.__class__.SUBSET_GLYPH_KWARGS.items():
            if _key in self.sources.keys():
                # determine responding attributes
                _responding = list(_dict["search"].keys())

                # create a field that holds search results that could be used elsewhere
                _num_points = len(self.sources[_key].data["label"])
                self._extra_source_cols[_key][SEARCH_SCORE_FIELD] = 0
                self.sources[_key].add([0] * _num_points, SEARCH_SCORE_FIELD)

                # make attributes respond to search
                for _flag, _params in _dict["search"].items():
                    self.glyph_kwargs[_key] = self.activate_search(
                        _key,
                        self.glyph_kwargs[_key],
                        altered_param=_params,
                    )
                if verbose:
                    self._info(
                        f"Activated {_responding} on subset {_key} to respond to the search widgets."
                    )

    @abstractmethod
    def activate_search(self, subset, kwargs, altered_param=("size", 10, 5, 7)):
        """
        ???+ note "Left to child classes that have a specific feature format."

            | Param           | Type    | Description                   |
            | :-------------- | :------ | :---------------------------  |
            | `subset`        | `str`   | the subset to activate search on |
            | `kwargs`        | `bool`  | kwargs for the plot to add to |
            | `altered_param` | `tuple` | (attribute, positive, negative, default) |
        """
        pass

    def _prelink_check(self, other):
        """
        ???+ note "Sanity check before linking two explorers."
            | Param   | Type    | Description                    |
            | :------ | :------ | :----------------------------- |
            | `other` | `BokehBaseExplorer` | the other explorer |
        """
        assert other is not self, "Self-loops are fordidden"
        assert isinstance(other, BokehBaseExplorer), "Must link to BokehBaseExplorer"

    def link_selection(self, key, other, other_key):
        """
        ???+ note "Synchronize the selected indices between specified sources."
            | Param   | Type    | Description                    |
            | :------ | :------ | :----------------------------- |
            | `key`   | `str`   | the key of the subset to link  |
            | `other` | `BokehBaseExplorer` | the other explorer |
            | `other_key` | `str` | the key of the other subset  |
        """
        self._prelink_check(other)
        # link selection in a bidirectional manner
        sl, sr = self.sources[key], other.sources[other_key]

        def left_to_right(attr, old, new):
            sr.selected.indices = sl.selected.indices[:]

        def right_to_left(attr, old, new):
            sl.selected.indices = sr.selected.indices[:]

        sl.selected.on_change("indices", left_to_right)
        sr.selected.on_change("indices", right_to_left)

        # link last manual selections (pointing to the same set)
        self._last_selections[key].union(other._last_selections[other_key])

        # link selection filter functions (pointing to the same set)
        self._selection_filters[key].data.update(
            other._selection_filters[other_key].data
        )
        self._selection_filters[key].union(other._selection_filters[other_key])

    def link_selection_options(self, other):
        """
        ???+ note "Synchronize the selection option values between explorers."
            | Param   | Type    | Description                    |
            | :------ | :------ | :----------------------------- |
            | `other` | `BokehBaseExplorer` | the other explorer |
        """

        def left_to_right(attr, old, new):
            other.selection_option_box.active = self.selection_option_box.active[:]

        def right_to_left(attr, old, new):
            self.selection_option_box.active = other.selection_option_box.active[:]

        self.selection_option_box.on_change("active", left_to_right)
        other.selection_option_box.on_change("active", right_to_left)

    def link_xy_range(self, other):
        """
        ???+ note "Synchronize plotting ranges on the xy-plane."
            | Param   | Type    | Description                    |
            | :------ | :------ | :----------------------------- |
            | `other` | `BokehBaseExplorer` | the other explorer |
        """
        self._prelink_check(other)
        # link coordinate ranges in a bidirectional manner
        for _attr in ["start", "end"]:
            self.figure.x_range.js_link(_attr, other.figure.x_range, _attr)
            self.figure.y_range.js_link(_attr, other.figure.y_range, _attr)
            other.figure.x_range.js_link(_attr, self.figure.x_range, _attr)
            other.figure.y_range.js_link(_attr, self.figure.y_range, _attr)

    @abstractmethod
    def plot(self, *args, **kwargs):
        """
        ???+ note "Plot something onto the figure."
            Implemented in child classes based on their functionalities.
            | Param      | Type  | Description           |
            | :--------- | :---- | :-------------------- |
            | `*args`    |       | left to child classes |
            | `**kwargs` |       | left to child classes |
        """
        pass

    def auto_color_mapping(self):
        """
        ???+ note "Find all labels and an appropriate color for each."
        """
        from hover.utils.bokeh_helper import auto_label_color

        labels = set()
        for _key in self.dfs.keys():
            labels = labels.union(set(self.dfs[_key]["label"].values))

        return auto_label_color(labels)
示例#7
0
    for c in plotted.copy():
        if c not in new:
            print('-', checkbox.labels[c])
            # del_dateline(checkbox.labels[c])
            plotted.remove(c)
    layout.children[1].children=[plot_t(), plot_c()]

def clearcountries():
    checkbox.active=[]

def plottype_handler(new):
    layout.children[1].children[0]=plot_t()

def c_period_handler(attr, old, new):
    calc_lastP(int(new))
    for country, pop in population.items():
        if country+'_lastP' in plottimeline:
            del plottimeline[country+'_lastP']
    layout.children[1].children[1]=plot_c()

checkbox.on_click(update_country)
btn_clear.on_click(clearcountries)
btng_main.on_click(plottype_handler)
slide_c_period.on_change('value', c_period_handler)

# Set up layouts and add to document
inputs = column(btng_main, slide_c_period, btn_clear, checkbox)
outputs = column(plot_t(),plot_c())
layout = row(inputs,outputs)
curdoc().add_root(layout)
curdoc().title = "Plot"
示例#8
0
class Widgets:
    def __init__(self, renderer):
        self.file_input = Dropdown(label="Select dataset", menu=[])
        for dataset_name in SQLDBInformer(DbConn({})).get_all_schemas():
            if dataset_name in set(["information_schema", "performance_schema", "sys", "defaultDB", "mysql"]):
                continue
            if dataset_name[:3] == "pg_":
                continue
            self.file_input.menu.append(dataset_name)
        self.run_id_dropdown = Dropdown(label="select run id here", menu=[])
        self.ground_truth_id_dropdown = Dropdown(label="select ground truth id here", menu=[])
        self.score_slider = RangeSlider(start=0, end=1, value=(0, 1), step=.1, callback_policy='mouseup',
                                        title="score range")
        self.max_elements_slider = Slider(start=1000, end=100000, value=10000, step=1000,
                                          callback_policy='mouseup', title="max render")
        self.range_link_radio = RadioGroup(labels=["Link read plot to x-range", "Link read plot to y-range"],
                                           active=0, orientation="horizontal")
        self.full_render_button = Button(label="render without limit")
        self.render_mems_button = Button(label="render MEMs")
        self.delete_button = Button(label="Delete Dataset")
        self.force_read_id = TextInput(value="", title="Render reads with ids (comma seperated list):")

        self.file_input.on_change("value", lambda x,y,z: self.file_input_change(renderer))
        self.run_id_dropdown.on_change("value", lambda x,y,z: self.run_id_change(renderer))
        self.ground_truth_id_dropdown.on_change("value", lambda x,y,z: self.ground_id_change(renderer))
        self.score_slider.on_change("value_throttled", lambda x,y,z: self.slider_change(renderer))
        self.max_elements_slider.on_change("value_throttled", lambda x,y,z: self.slider_change(renderer))

        self.full_render_button.on_event(ButtonClick, lambda x: self.full_render(renderer))
        self.render_mems_button.on_event(ButtonClick, lambda x: self.render_mems_button_event(renderer))
        self.delete_button.on_event(ButtonClick, lambda x: self.delete_button_event(renderer))
        self.force_read_id.on_change("value", lambda x,y,z: self.forced_read_ids_change(renderer))

        self.spinner_div = Div(text=html_file("spinner"), sizing_mode="scale_both", visible=False)
        self.condition = threading.Condition()

        self.subset_buttons = CheckboxButtonGroup(labels=["Render false-positives", "Render false-negatives",
                                                          "Render true-positives", "Compute Stats"],
                                                          active=[0, 1, 2])
        self.subset_buttons.on_click(lambda x: self.forced_read_ids_change(renderer))
        self.blur_slider = Slider(start=0, end=500, value=100, step=1, callback_policy='mouseup',
                                        title="Blur")
        self.blur_slider.on_change("value_throttled", lambda x,y,z: self.slider_change(renderer))

    def show_spinner(self, renderer):
        self.spinner_div.visible = True

    def hide_spinner(self, renderer):
        self.spinner_div.visible = False

    def file_input_change(self, renderer):
        with self.condition:
            self.file_input.label = "Selected dataset: " + self.file_input.value
            renderer.setup()

    def run_id_change(self, renderer):
        with self.condition:
            renderer.read_plot.recalc_stat = True
            print("new run_id:", self.run_id_dropdown.value)
            renderer.cached_global_overview = None
            run_table = SvCallerRunTable(renderer.db_conn)
            self.run_id_dropdown.label = "Selected run: " + run_table.getName(int(self.run_id_dropdown.value)) + \
                                            " - " + self.run_id_dropdown.value
            call_table = SvCallTable(renderer.db_conn)
            self.score_slider.end = 0
            if call_table.num_calls(int(self.run_id_dropdown.value), 0) > 0:
                self.score_slider.end = call_table.max_score(int(self.run_id_dropdown.value)) + 1
                self.score_slider.value = (0, self.score_slider.end)
            renderer.render(ignorable=False)

    def ground_id_change(self, renderer):
        with self.condition:
            renderer.read_plot.recalc_stat = True
            run_table = SvCallerRunTable(renderer.db_conn)
            self.ground_truth_id_dropdown.label = "Selected ground truth: " + \
                                                    run_table.getName(int(self.ground_truth_id_dropdown.value)) + \
                                                    " - " + self.ground_truth_id_dropdown.value
            renderer.render(ignorable=False)

    def slider_change(self, renderer):
        renderer.read_plot.recalc_stat = True
        renderer.render(ignorable=False)

    def forced_read_ids_change(self, renderer):
        renderer.render(ignorable=False)

    def full_render(self, renderer):
        renderer.render(render_all=True, ignorable=False)

    def get_blur(self):
        return self.blur_slider.value
    def get_render_f_p(self):
        return 0 in self.subset_buttons.active

    def get_render_f_n(self):
        return 1 in self.subset_buttons.active

    def get_render_t_p(self):
        return 2 in self.subset_buttons.active

    def compute_stats(self):
        return 3 in self.subset_buttons.active

    def get_forced_read_ids(self, renderer):
        if len(self.force_read_id.value) == 0:
            return []
        read_table = ReadTable(renderer.db_conn)
        ret = []
        for id_n_name in self.force_read_id.value.split(";"):
            split = id_n_name.split(":")
            if not len(split) == 2:
                continue
            seq_id, name = split
            idx = read_table.get_read_id(int(seq_id), name)
            if idx == -1:
                print(name, "does not exist in DB")
                continue
            ret.append(idx)
        return ret

    def render_mems_button_event(self, renderer):
        if not renderer.selected_read_id is None:
            read = ReadTable(renderer.db_conn).get_read(renderer.selected_read_id)

            seed_plot_y_s = int(max(renderer.read_plot.plot.y_range.start, 0))
            seed_plot_y_e = int(min(renderer.read_plot.plot.y_range.end, len(read)))
            seed_plot_x_s = int(max(renderer.read_plot.plot.x_range.start, 0))
            seed_plot_x_e = int(min(renderer.read_plot.plot.x_range.end, renderer.pack.unpacked_size_single_strand))

            filtered_mems = compute_seeds_area(renderer.params, seed_plot_x_s,
                                               seed_plot_y_s, seed_plot_x_e-seed_plot_x_s,
                                               seed_plot_y_e-seed_plot_y_s, read, renderer.pack)
            seed_data_new = dict((key, []) for key in renderer.read_plot.seeds.data.keys())
            if len(filtered_mems) > 0:
                max_seed_size = max( seed.size for seed in filtered_mems )
                for idx in range(len(filtered_mems)):
                    add_seed(filtered_mems[idx], seed_data_new, max_seed_size, [], [],
                                0, False, -1, renderer.selected_read_id, idx, read.name)
            renderer.read_plot.seeds.data = seed_data_new

    def delete_button_event(self, renderer):
        print("unimplemented at the moment...")
        #renderer.sv_db.delete_run(renderer.get_run_id())
        renderer.setup()
            None
    elif HPC and ('Nuclear with HPC' not in showcols):
        showcols.append('Nuclear with HPC')

    if 'All other' not in showcols:
        try:
            showcols.remove('All other - including lagoon')
        except:
            None
    elif lagoon and ('All other - including lagoon' not in showcols):
        showcols.append('All other - including lagoon')

    showLines(showcols)


checkbuttons.on_click(checkboxCallback)

HPCButton = Toggle(label='Add Hinkley Point C', button_type='default')


def addHPC(clicked):
    global HPC
    HPC = clicked
    if HPC and ('Nuclear' not in showcols):
        showcols.append('Nuclear')
    if HPC:
        showcols.append('Nuclear with HPC')
        HPCButton.button_type = 'success'
    elif 'Nuclear with HPC' in showcols:
        showcols.remove('Nuclear with HPC')
    if not HPC:
示例#10
0
# TODO: enable widgets that support multi-selection
# Elements selection widget from a periodic table

code = Select(title='Code', value=codes[0], options=codes)
code.on_change('value', lambda attr, old, new: CF.update_code())

exchange = Select(title='ExchangeCorrelation',
                  value=exchanges[0],
                  options=exchanges)
exchange.on_change('value', lambda attr, old, new: CF.update_exchange())

struct = Select(title='Structure', value=structures[0], options=structures)
struct.on_change('value', lambda attr, old, new: CF.update_struct())

element = CheckboxButtonGroup(labels=_elements, active=[1])
element.on_click(CF.update_element)

prop = Select(title='Property', value=properties[0], options=properties)
prop.on_change('value', lambda attr, old, new: CF.update_prop())

apply_crossfilter = Button(label='CrossFilter and Plot')
apply_crossfilter.on_click(CF.update_crossfilter)

clean_crossfilter = Button(label='Clear')
clean_crossfilter.on_click(CF.clear_crossfilter)

x_select.on_change('value', lambda attr, old, new: CF.update_x())

y_select.on_change('value', lambda attr, old, new: CF.update_y())

analyse_crossfilt = Button(label='PadeAnalysis')
示例#11
0
# define the selection widgets for code, exchange,
# TODO: enable widgets that support multi-selection
# Elements selection widget from a periodic table

code = Select(title='Code', value=codes[0], options=codes)
code.on_change('value', lambda attr, old, new: CF.update_code())

exchange = Select(title='ExchangeCorrelation', value=exchanges[0], options=exchanges)
exchange.on_change('value', lambda attr, old, new: CF.update_exchange())

struct = Select(title='Structure', value=structures[0], options=structures)
struct.on_change('value', lambda attr, old, new: CF.update_struct())

element = CheckboxButtonGroup(labels=_elements, active=[1])
element.on_click(CF.update_element)

prop = Select(title='Property', value=properties[0], options=properties)
prop.on_change('value', lambda attr, old, new: CF.update_prop())

apply_crossfilter = Button(label='CrossFilter and Plot')
apply_crossfilter.on_click(CF.update_crossfilter)

clean_crossfilter = Button(label='Clear')
clean_crossfilter.on_click(CF.clear_crossfilter)

x_select.on_change('value', lambda attr, old, new: CF.update_x())

y_select.on_change('value', lambda attr, old, new: CF.update_y())

analyse_crossfilt = Button(label='PadeAnalysis')
示例#12
0
class BokehForLabeledText(Loggable, ABC):
    """
    Base class that keeps template explorer settings.

    Assumes:

    - in supplied dataframes
      - (always) text data in a `text` column
      - (always) xy coordinates in `x` and `y` columns
      - (always) an index for the rows
      - (likely) classification label in a `label` column

    Does not assume:

    - what the explorer serves to do.
    """

    DEFAULT_FIGURE_KWARGS = {
        "tools": [
            # change the scope
            "pan",
            "wheel_zoom",
            # make selections
            "tap",
            "poly_select",
            "lasso_select",
            # make inspections
            "hover",
            # navigate changes
            "undo",
            "redo",
        ],
        # inspection details
        "tooltips":
        bokeh_hover_tooltip(label=True,
                            text=True,
                            image=False,
                            coords=True,
                            index=True),
        # bokeh recommends webgl for scalability
        "output_backend":
        "webgl",
    }

    DATA_KEY_TO_KWARGS = {}

    MANDATORY_COLUMNS = ["text", "label", "x", "y"]

    def __init__(self, df_dict, **kwargs):
        """
        Operations shared by all child classes.

        - settle the figure settings by using child class defaults & kwargs overrides
        - settle the glyph settings by using child class defaults
        - create widgets that child classes can override
        - create data sources the correspond to class-specific data subsets.
        - activate builtin search callbacks depending on the child class.
        - create a (typically) blank figure under such settings
        """
        self.figure_kwargs = self.__class__.DEFAULT_FIGURE_KWARGS.copy()
        self.figure_kwargs.update(kwargs)
        self.glyph_kwargs = {
            _key: _dict["constant"].copy()
            for _key, _dict in self.__class__.DATA_KEY_TO_KWARGS.items()
        }
        self._setup_widgets()
        self._setup_dfs(df_dict)
        self._setup_sources()
        self._activate_search_builtin()
        self.figure = figure(**self.figure_kwargs)
        self.reset_figure()

    @classmethod
    def from_dataset(cls, dataset, subset_mapping, *args, **kwargs):
        """
        Construct from a SupervisableDataset.
        """
        # local import to avoid import cycles
        from hover.core.dataset import SupervisableDataset

        assert isinstance(dataset, SupervisableDataset)
        df_dict = {_v: dataset.dfs[_k] for _k, _v in subset_mapping.items()}
        return cls(df_dict, *args, **kwargs)

    def reset_figure(self):
        """Start over on the figure."""
        self._info("Resetting figure")
        self.figure.renderers.clear()

    def _setup_widgets(self):
        """
        Prepare widgets for interactive functionality.

        Create positive/negative text search boxes.
        """
        from bokeh.models import TextInput, CheckboxButtonGroup

        # set up text search widgets, without assigning callbacks yet
        # to provide more flexibility with callbacks
        self._info("Setting up widgets")
        self.search_pos = TextInput(
            title="Text contains (plain text, or /pattern/flag for regex):",
            width_policy="fit",
            height_policy="fit",
        )
        self.search_neg = TextInput(title="Text does not contain:",
                                    width_policy="fit",
                                    height_policy="fit")

        # set up subset display toggles which do have clearly defined callbacks
        data_keys = list(self.__class__.DATA_KEY_TO_KWARGS.keys())
        self.data_key_button_group = CheckboxButtonGroup(
            labels=data_keys, active=list(range(len(data_keys))))

        def update_data_key_display(active):
            visible_keys = {
                self.data_key_button_group.labels[idx]
                for idx in active
            }
            for _renderer in self.figure.renderers:
                # if the renderer has a name "on the list", update its visibility
                if _renderer.name in self.__class__.DATA_KEY_TO_KWARGS.keys():
                    _renderer.visible = _renderer.name in visible_keys

        # store the callback (useful, for example, during automated tests) and link it
        self.update_data_key_display = update_data_key_display
        self.data_key_button_group.on_click(self.update_data_key_display)

    def _layout_widgets(self):
        """Define the layout of widgets."""
        return column(self.search_pos, self.search_neg,
                      self.data_key_button_group)

    def view(self):
        """Define the layout of the whole explorer."""
        return column(self._layout_widgets(), self.figure)

    def _setup_dfs(self, df_dict, copy=False):
        """
        Check and store DataFrames BY REFERENCE BY DEFAULT.

        Intended to be extended in child classes for pre/post processing.
        """
        self._info("Setting up DataFrames")
        supplied_keys = set(df_dict.keys())
        expected_keys = set(self.__class__.DATA_KEY_TO_KWARGS.keys())

        # perform high-level df key checks
        supplied_not_expected = supplied_keys.difference(expected_keys)
        expected_not_supplied = expected_keys.difference(supplied_keys)

        for _key in supplied_not_expected:
            self._warn(
                f"{self.__class__.__name__}.__init__(): got unexpected df key {_key}"
            )
        for _key in expected_not_supplied:
            self._warn(
                f"{self.__class__.__name__}.__init__(): missing expected df key {_key}"
            )

        # create df with column checks
        self.dfs = dict()
        for _key, _df in df_dict.items():
            if _key in expected_keys:
                for _col in self.__class__.MANDATORY_COLUMNS:
                    if _col not in _df.columns:
                        # edge case: DataFrame has zero rows
                        assert (
                            _df.shape[0] == 0
                        ), f"Missing column '{_col}' from non-empty {_key} DataFrame: found {list(_df.columns)}"
                        _df[_col] = None

                self.dfs[_key] = _df.copy() if copy else _df

    def _setup_sources(self):
        """
        Create (NOT UPDATE) ColumnDataSource objects.

        Intended to be extended in child classes for pre/post processing.
        """
        self._info("Setting up sources")
        self.sources = {
            _key: ColumnDataSource(_df)
            for _key, _df in self.dfs.items()
        }

    def _update_sources(self):
        """
        Update the sources with the corresponding dfs.

        Note that it seems mandatory to re-activate the search widgets.
        This is because the source loses plotting kwargs.
        """
        for _key in self.dfs.keys():
            self.sources[_key].data = self.dfs[_key]
        self._activate_search_builtin(verbose=False)

    def _activate_search_builtin(self, verbose=True):
        """
        Typically called once during initialization.
        Highlight positive search results and mute negative search results.

        Note that this is a template method which heavily depends on class attributes.
        """
        for _key, _dict in self.__class__.DATA_KEY_TO_KWARGS.items():
            if _key in self.sources.keys():
                _responding = list(_dict["search"].keys())
                for _flag, _params in _dict["search"].items():
                    self.glyph_kwargs[_key] = self.activate_search(
                        self.sources[_key],
                        self.glyph_kwargs[_key],
                        altered_param=_params,
                    )
                if verbose:
                    self._info(
                        f"Activated {_responding} on subset {_key} to respond to the search widgets."
                    )

    def activate_search(self, source, kwargs,
                        altered_param=("size", 10, 5, 7)):
        """
        Enables string/regex search-and-highlight mechanism.

        Modifies the plotting source in-place.
        """
        assert isinstance(source, ColumnDataSource)
        assert isinstance(kwargs, dict)
        updated_kwargs = kwargs.copy()

        param_key, param_pos, param_neg, param_default = altered_param
        num_points = len(source.data["text"])
        default_param_list = [param_default] * num_points
        source.add(default_param_list, f"{param_key}")

        updated_kwargs[param_key] = param_key

        search_callback = CustomJS(
            args={
                "source": source,
                "key_pos": self.search_pos,
                "key_neg": self.search_neg,
                "param_pos": param_pos,
                "param_neg": param_neg,
                "param_default": param_default,
            },
            code=f"""
            const data = source.data;
            const text = data['text'];
            var arr = data['{param_key}'];
            """ + """
            var search_pos = key_pos.value;
            var search_neg = key_neg.value;
            var valid_pos = (search_pos.length > 0);
            var valid_neg = (search_neg.length > 0);

            function determineAttr(candidate)
            {
                var score = 0;
                if (valid_pos) {
                    if (candidate.search(search_pos) >= 0) {
                        score += 1;
                    } else {
                        score -= 2;
                    }
                };
                if (valid_neg) {
                    if (candidate.search(search_neg) < 0) {
                        score += 1;
                    } else {
                        score -= 2;
                    }
                };
                if (score > 0) {
                    return param_pos;
                } else if (score < 0) {
                    return param_neg;
                } else {return param_default;}
            }

            function toRegex(search_key) {
                var match = search_key.match(new RegExp('^/(.*?)/([gimy]*)$'));
                if (match) {
                    return new RegExp(match[1], match[2]);
                } else {
                    return search_key;
                }
            }

            if (valid_pos) {search_pos = toRegex(search_pos);}
            if (valid_neg) {search_neg = toRegex(search_neg);}
            for (var i = 0; i < arr.length; i++) {
                arr[i] = determineAttr(text[i]);
            }

            source.change.emit()
            """,
        )

        self.search_pos.js_on_change("value", search_callback)
        self.search_neg.js_on_change("value", search_callback)
        return updated_kwargs

    def _prelink_check(self, other):
        """
        Sanity check before linking two explorers.
        """
        assert other is not self, "Self-loops are fordidden"
        assert isinstance(
            other, BokehForLabeledText), "Must link to BokehForLabelText"

    def link_selection(self, key, other, other_key):
        """
        Sync the selected indices between specified sources.
        """
        self._prelink_check(other)
        # link selection in a bidirectional manner
        sl, sr = self.sources[key], other.sources[other_key]
        sl.selected.js_link("indices", sr.selected, "indices")
        sr.selected.js_link("indices", sl.selected, "indices")

    def link_xy_range(self, other):
        """
        Sync plotting ranges on the xy-plane.
        """
        self._prelink_check(other)
        # link coordinate ranges in a bidirectional manner
        for _attr in ["start", "end"]:
            self.figure.x_range.js_link(_attr, other.figure.x_range, _attr)
            self.figure.y_range.js_link(_attr, other.figure.y_range, _attr)
            other.figure.x_range.js_link(_attr, self.figure.x_range, _attr)
            other.figure.y_range.js_link(_attr, self.figure.y_range, _attr)

    @abstractmethod
    def plot(self, *args, **kwargs):
        """
        Plot something onto the figure.
        """
        pass

    def auto_labels_cmap(self):
        """
        Find all labels and an appropriate color map.
        """
        labels = set()
        for _key in self.dfs.keys():
            labels = labels.union(set(self.dfs[_key]["label"].values))
        labels.discard(module_config.ABSTAIN_DECODED)
        labels = sorted(labels, reverse=True)

        assert len(labels) <= 20, "Too many labels to support (max at 20)"
        cmap = "Category10_10" if len(labels) <= 10 else "Category20_20"
        return labels, cmap

    def auto_legend_correction(self):
        """
        Find legend items and deduplicate by label.
        """
        if not hasattr(self.figure, "legend"):
            self._fail(
                "Attempting auto_legend_correction when there is no legend")
            return
        # extract all items and start over
        items = self.figure.legend.items[:]
        self.figure.legend.items.clear()

        # use one item to hold all renderers matching its label
        label_to_item = OrderedDict()

        for _item in items:
            _label = _item.label.get("value", "")
            if _label not in label_to_item.keys():
                label_to_item[_label] = _item
            else:
                label_to_item[_label].renderers.extend(_item.renderers)

        # assign deduplicated items back to the legend
        self.figure.legend.items = list(label_to_item.values())
        return
示例#13
0
renderer['lines'] = p2.line(x="xs", y="ys", line_width=2, source=d2)


def callback_glyphs(new):
    print("Active Checkboxes:", new)
    renderer['circles'].visible = False
    renderer['lines'].visible = False

    if 0 in new:
        renderer['circles'].visible = True
    if 1 in new:
        renderer['lines'].visible = True


btn_glyphs = CheckboxButtonGroup(labels=["Circles", "Lines"], active=[0, 1])
btn_glyphs.on_click(callback_glyphs)
"""
=======================
Two Axes
=======================

"""

d_axis_1 = ColumnDataSource(dict(xs=np.arange(0, 10), ys=np.random.rand(10)))

d_axis_2 = ColumnDataSource(
    dict(xs=np.arange(0, 10), ys=np.random.rand(10) * 100))

p_axis = figure(plot_width=400, plot_height=400, y_range=(0.0, 1.0))
p_axis.line(x="xs", y="ys", line_width=2, source=d_axis_1)