Source code for bowtie.visual

# -*- coding: utf-8 -*-
"""Visual components."""

from typing import Dict, Optional, List, Union

from bowtie._component import Component, jdumps, jsbool
from bowtie._progress import Progress


# pylint: disable=too-few-public-methods
class _Visual(Component):
    """Abstract class all visual components inherit.

    Used to test if a an object is a visual component.
    """

    # pylint: disable=abstract-method
    def __init__(self) -> None:
        self.progress = Progress()
        super().__init__()

    @property
    def _instantiate(self) -> str:
        # pylint: disable=protected-access
        begin, end = self.progress._tags
        tagwrap = begin + '{component}' + self._tagbase + end
        return self._insert(tagwrap, self._comp)


[docs]class Table(_Visual): """Ant Design table with filtering and sorting.""" _TEMPLATE = 'table.jsx' _COMPONENT = 'AntTable' _PACKAGE = None _ATTRS = ('columns={{{columns}}} ' 'resultsPerPage={{{results_per_page}}}') def __init__(self, data=None, columns: Optional[List[Union[int, str]]] = None, results_per_page: int = 10) -> None: """Create a table and optionally initialize the data. Parameters ---------- data : pd.DataFrame, optional columns : list, optional List of column names to display. results_per_page : int, optional Number of rows on each pagination of the table. """ super().__init__() self.data = [] self.columns = [] if data: self.data, self.columns = self._make_data(data) elif columns: self.columns = self._make_columns(columns) self.results_per_page = results_per_page self._comp = self._tag.format( columns=self.columns, results_per_page=self.results_per_page ) @staticmethod def _make_columns(columns): """Transform list of columns into AntTable format.""" return [dict(title=str(c), dataIndex=str(c), key=str(c)) for c in columns] @staticmethod def _make_data(data): """Transform table data into JSON.""" jsdata = [] for idx, row in data.iterrows(): row.index = row.index.astype(str) rdict = row.to_dict() rdict.update(dict(key=str(idx))) jsdata.append(rdict) return jsdata, Table._make_columns(data.columns) # pylint: disable=no-self-use
[docs] def do_data(self, data): """Replace the columns and data of the table. Parameters ---------- data : pandas.DataFrame Returns ------- None """
return self._make_data(data)
[docs] def do_columns(self, columns): """Update the columns of the table. Parameters ---------- columns : array-like List of strings. Returns ------- None """
return self._make_columns(columns)
[docs]class SmartGrid(_Visual): """Griddle table with filtering and sorting.""" _TEMPLATE = 'griddle.jsx' _COMPONENT = 'SmartGrid' _PACKAGE = 'griddle-react@1.11.2' _ATTRS = None def __init__(self) -> None: """Create the table, optionally set the columns.""" super().__init__() self._comp = self._tag # pylint: disable=no-self-use
[docs] def do_update(self, data): """Update the data of the table. Parameters ---------- data : pandas.DataFrame Returns ------- None """
return data.to_dict(orient='records') def get(self, data): """ Get the table data. Returns ------- list Each entry in the list is a dict of labels and values for a row. """
return data
[docs]class SVG(_Visual): """SVG image. Mainly for matplotlib plots. """ _TEMPLATE = 'svg.jsx' _COMPONENT = 'SVG' _PACKAGE = None _ATTRS = 'preserveAspectRatio={{{preserve_aspect_ratio}}}' def __init__(self, preserve_aspect_ratio: bool = False) -> None: """Create SVG component. Parameters ---------- preserve_aspect_ratio : bool, optional If ``True`` it preserves the aspect ratio otherwise it will stretch to fill up the space available. """ super().__init__() self.preserve_aspect_ratio = preserve_aspect_ratio self._comp = self._tag.format( preserve_aspect_ratio=jsbool(self.preserve_aspect_ratio) ) # pylint: disable=no-self-use
[docs] def do_image(self, image): """Replace the image. Parameters ---------- image : str Generated by ``savefig`` from matplotlib with ``format=svg``. Returns ------- None Examples -------- This shows how to update an SVG widget with Matplotlib. >>> from io import StringIO >>> import matplotlib >>> matplotlib.use('Agg') >>> import matplotlib.pyplot as plt >>> image = SVG() >>> >>> def callback(x): ... sio = StringIO() ... plt.plot(range(5)) ... plt.savefig(sio, format='svg') ... sio.seek(0) ... s = sio.read() ... idx = s.find('<svg') ... s = s[idx:] ... image.do_image(s) """
return image
[docs]class Plotly(_Visual): """Plotly component. Useful for many kinds of plots. """ _TEMPLATE = 'plotly.jsx' _COMPONENT = 'PlotlyPlot' _PACKAGE = 'plotly.js@1.34.0' _ATTRS = 'initState={{{init}}}' def __init__(self, init: Optional[Dict] = None) -> None: """Create a Plotly component. Parameters ---------- init : dict, optional Initial Plotly data to plot. """ super().__init__() if init is None: init = dict(data=[], layout={'autosize': False}) self.init = init self._comp = self._tag.format( init=jdumps(self.init) ) # Events def on_click(self): """React to Plotly click event. | **Payload:** TODO. Returns ------- str Name of event. """ return self.get_click def on_beforehover(self): """Emit an event before hovering over a point. | **Payload:** TODO. Returns ------- str Name of event. """ return self.get_hover def on_hover(self): """Emit an event after hovering over a point. | **Payload:** TODO. Returns ------- str Name of event. """ return self.get_hover def on_unhover(self): """Emit an event when hover is removed. | **Payload:** TODO. Returns ------- str Name of event. """ return self.get_hover def on_select(self): """Emit an event when points are selected with a tool. | **Payload:** TODO. Returns ------- str Name of event. """ return self.get_select def on_relayout(self): """Emit an event when the chart axes change. | **Payload:** TODO. Returns ------- str Name of event. """ return self.get_layout # Commands # pylint: disable=no-self-use
[docs] def do_all(self, plot): """Replace the entire plot. Parameters ---------- plot : dict Dict that can be plotted with Plotly. It should have this structure: ``{data: [], layout: {}}``. Returns ------- None """
return plot
[docs] def do_data(self, data): """Replace the data portion of the plot. Parameters ---------- data : list of traces List of data to replace the old data. Returns ------- None """
return data
[docs] def do_layout(self, layout): """Update the layout. Parameters ---------- layout : dict Contains layout information. Returns ------- None """
return layout
[docs] def do_config(self, config): """Update the configuration of the plot. Parameters ---------- config : dict Plotly config information. Returns ------- None """
return config def get(self, data): """Get the current selection of points. Returns ------- list """ return data def get_select(self, data): """Get the current selection of points. Returns ------- list """ return data def get_click(self, data): """Get the current selection of points. Returns ------- list """ return data def get_hover(self, data): """Get the current selection of points. Returns ------- list """ return data def get_layout(self, data): """Get the current layout. Returns ------- list """
return data