Esempio n. 1
0
class VueMultiselectWidget(InputWidget):
    __implementation__ = os.path.join(os.path.dirname(__file__),
                                      'vue_multiselect.js')
    __javascript__ = [
        'https://cdn.jsdelivr.net/npm/vue/dist/vue.js',
        'https://cdn.jsdelivr.net/npm/[email protected]/index.min.js',
        'https://unpkg.com/axios/dist/axios.min.js',
        'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-multiselect.min.js'
    ]
    __css__ = [
        'https://unpkg.com/[email protected]/dist/vue-multiselect.min.css'
    ]

    selectedOptions = List(
        Dict(String(),
             Any(),
             help="""
    Initial value in the multiselect
    # """))

    options = List(Dict(String(), Any()),
                   help="""
    List of dropdown options
    """,
                   default=[])

    modelName = String(help='name of model you want to autocomplete against')
Esempio n. 2
0
class AbstractVTKPlot(HTMLBox):
    """
    Abstract Bokeh model for vtk plots that wraps around a vtk-js library and
    renders it inside a Bokeh plot.
    """

    __javascript__ = [vtk_cdn]

    __js_skip__ = {'vtk': [vtk_cdn]}

    __js_require__ = {
        "paths": {
            "vtk": vtk_cdn[:-3]
        },
        "exports": {
            "vtk": None
        },
        "shim": {
            "vtk": {
                "exports": "vtk"
            }
        }
    }

    renderer_el = Any(readonly=True)

    orientation_widget = Bool(default=False)

    camera = Dict(String, Any)

    height = Override(default=300)

    width = Override(default=300)
Esempio n. 3
0
def test_Property_wrap() -> None:
    types = [
        Bool(),
        Int(),
        Float(),
        Complex(),
        String(),
        Enum("Some", "a", "b"),
        Color(),
        Regex("^$"),
        Seq(Any),
        Tuple(Any, Any),
        Instance(_TestModel),
        Any(),
        Interval(Float, 0, 1),
        Either(Int, String),
        DashPattern(),
        Size(),
        Percent(),
        Angle(),
        MinMaxBounds(),
    ]

    for x in types:
        for y in (0, 1, 2.3, "foo", None, (), [], {}):
            r = x.wrap(y)
            assert r == y
            assert isinstance(r, type(y))
Esempio n. 4
0
File: vtk.py Progetto: xtaje/panel
class VTKPlot(HTMLBox):
    """
    A Bokeh model that wraps around a vtk-js library and renders it inside
    a Bokeh plot.
    """

    __javascript__ = [vtk_cdn]

    __js_require__ = {"paths": {"vtk": vtk_cdn[:-3]},
                      "shim": {"vtk": {"exports": "vtk"}}}

    __implementation__ = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'vtk.ts')

    append = Bool(default=False)

    data = String(help="""The serialized vtk.js data""")

    camera = Dict(String, Any)

    enable_keybindings = Bool(default=False)

    orientation_widget = Bool(default=False)

    renderer_el = Any(readonly=True)

    height = Override(default=300)

    width = Override(default=300)
Esempio n. 5
0
class VTKVolumePlot(HTMLBox):
    """
    A Bokeh model that wraps around a vtk-js library and renders it inside
    a Bokeh plot.
    """

    __javascript__ = [vtk_cdn]

    __js_require__ = {
        "paths": {
            "vtk": vtk_cdn[:-3]
        },
        "shim": {
            "vtk": {
                "exports": "vtk"
            }
        }
    }

    actor = Any(readonly=True)

    data = Dict(String, Any)

    height = Override(default=300)

    width = Override(default=300)
Esempio n. 6
0
class JsonEditor(InputWidget):
    # The special class attribute ``__implementation__`` should contain a string
    # of JavaScript (or CoffeeScript) code that implements the JavaScript side
    # of the custom extension model or a string name of a JavaScript (or
    # CoffeeScript) file with the implementation.

    __implementation__ = 'json_editor.ts'
    __javascript__ = ["straxui/static/jsoneditor.min.js"]
    __css__ = ["straxui/static/jsoneditor.min.css"]

    # Below are all the "properties" for this model. Bokeh properties are
    # class attributes that define the fields (and their types) that can be
    # communicated automatically between Python and the browser. Properties
    # also support type validation. More information about properties in
    # can be found here:
    #
    #    https://bokeh.pydata.org/en/latest/docs/reference/core.html#bokeh-core-properties

    disable = Bool(default=True,
                   help="""
    Enable or disable the editor.
    """)

    json = Any(default={},
               help="""
    The actual json string to edit.
    """)
Esempio n. 7
0
class JSONEditor(HTMLBox):
    """
    A bokeh model that allows editing JSON.
    """

    data = Any()

    menu = Bool(True)

    mode = Enum("tree",
                "view",
                "form",
                "code",
                "text",
                "preview",
                default='tree')

    search = Bool(True)

    selection = List(Any)

    schema = Nullable(Dict(String, Any), default=None)

    templates = List(Any)

    __javascript_raw__ = [
        'https://cdn.jsdelivr.net/npm/[email protected]/dist/jsoneditor.min.js'
    ]

    __css_raw__ = [
        'https://cdn.jsdelivr.net/npm/[email protected]/dist/jsoneditor.min.css'
    ]

    @classproperty
    def __javascript__(cls):
        return bundled_files(cls)

    @classproperty
    def __css__(cls):
        return bundled_files(cls, 'css')

    @classproperty
    def __js_skip__(cls):
        return {'JSONEditor': cls.__javascript__}

    __js_require__ = {
        'paths': {
            'jsoneditor':
            "//cdn.jsdelivr.net/npm/[email protected]/dist/jsoneditor.min"
        },
        'exports': {
            'jsoneditor': 'JSONEditor'
        },
        'shim': {
            'jsoneditor': {
                'exports': "JSONEditor"
            }
        }
    }
Esempio n. 8
0
class IPyWidget(HTMLBox):

    bundle = Any()

    def __init__(self, widget, **kwargs):
        from ipywidgets import embed, Widget
        super().__init__(**kwargs)
        spec = widget.get_view_spec()
        state = Widget.get_manager_state(widgets=[])
        state["state"] = embed.dependency_state([widget], drop_defaults=True)
        self.bundle = dict(spec=spec, state=state)
Esempio n. 9
0
class VarModel(Model):
    """ Model to track a python global variable at both server and client
    side. Using this model a variable can be passed to others object's
    CustomJS callbacks as well as having defined it's own callback.

    Args:
        value (Any):
            A value for this variable of type Any().
    """
    __implementation__ = 'varModel.coffee'

    value = Any(default=None, help="A value for this variable of type Any().")
Esempio n. 10
0
class IPyWidget(HTMLBox):

    __javascript__ = [
        "https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js",
    ]

    bundle = Any()

    def __init__(self, *, widget: Widget, **kwargs):
        super().__init__(**kwargs)
        spec = widget.get_view_spec()
        state = Widget.get_manager_state(widgets=[])
        state["state"] = embed.dependency_state([widget], drop_defaults=True)
        self.bundle = dict(spec=spec, state=state)
Esempio n. 11
0
class Video(HTMLBox):

    loop = Bool(False, help="""Whether the video should loop""")

    paused = Bool(False, help="""Whether the video is paused""")

    time = Float(0, help="""
        The current time stamp of the video playback""")

    throttle = Int(250, help="""
        The frequency at which the time value is updated in milliseconds.""")

    value = Any(help="Encoded file data")

    volume = Int(0, help="""The volume of the video player.""")
Esempio n. 12
0
class VideoStream(HTMLBox):

    format = Enum('png', 'jpeg', default='png')

    paused = Bool(False, help="""Whether the video is paused""")

    snapshot = Bool(False, help="""On change generate a snapshot of the current video frame""")

    timeout = Float(None, help="""
        The timeout between snapshots (if None snapshot only generated
        when snapshot property is changed""")

    value = Any(help="""Snapshot Data""")

    height = Override(default=240)

    width = Override(default=320)
Esempio n. 13
0
class Audio(Widget):

    __implementation__ = os.path.join(
        os.path.abspath(os.path.dirname(__file__)), 'audio.ts')

    loop = Bool(False, help="""Whether the audio should loop""")

    paused = Bool(False, help="""Whether the audio is paused""")

    time = Float(0,
                 help="""
        The current time stamp of the audio playback""")

    throttle = Int(250,
                   help="""
        The frequency at which the time value is updated in milliseconds.""")

    value = Any(help="Encoded file data")

    volume = Int(0, help="""The volume of the audio player.""")
Esempio n. 14
0
File: vtk.py Progetto: xtaje/panel
class VTKVolumePlot(HTMLBox):
    """
    A Bokeh model that wraps around a vtk-js library and renders it inside
    a Bokeh plot.
    """

    __javascript__ = [vtk_cdn]

    __js_require__ = {"paths": {"vtk": vtk_cdn[:-3]},
                      "shim": {"vtk": {"exports": "vtk"}}}

    __implementation__ = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'vtkvolume.ts')

    actor = Any(readonly=True)

    data = Dict(String, Any)

    height = Override(default=300)

    width = Override(default=300)
Esempio n. 15
0
class VTKPlot(HTMLBox):
    """
    A Bokeh model that wraps around a vtk-js library and renders it inside
    a Bokeh plot.
    """

    __javascript__ = [vtk_cdn]

    __js_skip__ = {'vtk': [vtk_cdn]}

    __js_require__ = {
        "paths": {
            "vtk": vtk_cdn[:-3]
        },
        "exports": {
            "vtk": None
        },
        "shim": {
            "vtk": {
                "exports": "vtk"
            }
        }
    }

    append = Bool(default=False)

    data = String(help="""The serialized vtk.js data""")

    camera = Dict(String, Any)

    axes = Instance(VTKAxes)

    enable_keybindings = Bool(default=False)

    orientation_widget = Bool(default=False)

    renderer_el = Any(readonly=True)

    height = Override(default=300)

    width = Override(default=300)
Esempio n. 16
0
class VTKAxes(Model):
    """
    A Bokeh model for axes
    """

    xticker = Dict(String, Any)

    yticker = Dict(String, Any)

    zticker = Dict(String, Any)

    origin = Any()

    digits = Int(default=1)

    show_grid = Bool(default=True)

    grid_opacity = Float(default=0.1)

    axes_opacity = Float(default=1)

    fontsize = PositiveInt(default=12)
Esempio n. 17
0
class AggregateGlyph(NestedCompositeGlyph):
    """A base composite glyph for aggregating an array.

    Implements default stacking and dodging behavior that other composite
    glyphs can inherit.
    """

    x_label = String()
    x_label_value = Any()

    stack_label = String()
    stack_shift = Float(default=0.0)

    dodge_label = String(
        help="""Where on the scale the glyph should be placed.""")
    dodge_shift = Float(default=None)

    agg = Instance(Stat, default=Sum())

    span = Float(help="""The range of values represented by the aggregate.""")

    def __init__(self, x_label=None, **kwargs):

        label = kwargs.get('label')
        if x_label is not None:
            kwargs['x_label_value'] = x_label

            if not isinstance(x_label, str):
                x_label = str(x_label)

            kwargs['x_label'] = x_label
        elif label is not None:
            kwargs['x_label'] = str(label)

        super(AggregateGlyph, self).__init__(**kwargs)

    def get_dodge_label(self, shift=0.0):
        """Generate the label defining an offset in relation to a position on a scale."""
        if self.dodge_shift is None:
            shift_str = ':' + str(0.5 + shift)
        elif self.dodge_shift is not None:
            shift_str = ':' + str(self.dodge_shift + shift)
        else:
            shift_str = ''

        return str(label_from_index_dict(self.x_label)) + shift_str

    def filter_glyphs(self, glyphs):
        """Return only the glyphs that are of the same class."""
        return [glyph for glyph in glyphs if isinstance(glyph, self.__class__)]

    @staticmethod
    def groupby(glyphs, prop):
        """Returns a dict of `CompositeGlyph`s, grouped by unique values of prop.

        For example, if all glyphs had a value of 'a' or 'b' for glyph.prop, the dict
        would contain two keys, 'a' and 'b', where each value is a list of the glyphs
        that had each of the values.
        """
        grouped = defaultdict(list)
        labels = [getattr(glyph, prop) for glyph in glyphs]
        labels = [
            tuple(label.values()) if isinstance(label, dict) else label
            for label in labels
        ]
        [grouped[label].append(glyph) for label, glyph in zip(labels, glyphs)]
        labels = pd.Series(labels).drop_duplicates().values
        return labels, grouped

    def __stack__(self, glyphs):
        """Apply relative shifts to the composite glyphs for stacking."""
        filtered_glyphs = self.filter_glyphs(glyphs)
        labels, grouped = self.groupby(filtered_glyphs, 'x_label')

        for label in labels:
            group = grouped[label]

            # separate the negative and positive aggregates into separate groups
            neg_group = [glyph for glyph in group if glyph.span < 0]
            pos_group = [glyph for glyph in group if glyph.span >= 0]

            # apply stacking to each group separately
            for group in [neg_group, pos_group]:
                shift = []
                for i, glyph in enumerate(group):
                    # save off the top of each rect's height
                    shift.append(glyph.span)
                    if i > 0:
                        glyph.stack_shift = sum(shift[0:i])
                        glyph.refresh()

    def __dodge__(self, glyphs):
        """Apply relative shifts to the composite glyphs for dodging."""
        if self.dodge_label is not None:
            filtered_glyphs = self.filter_glyphs(glyphs)
            labels, grouped = self.groupby(filtered_glyphs, 'dodge_label')

            # calculate transformations
            step = np.linspace(0, 1.0, len(grouped.keys()) + 1, endpoint=False)
            width = min(0.2, (1. / len(grouped.keys()))**1.1)

            # set bar attributes and re-aggregate
            for i, label in enumerate(labels):
                group = grouped[label]
                for glyph in group:
                    glyph.dodge_shift = step[i + 1]
                    glyph.width = width
                    glyph.refresh()
Esempio n. 18
0
class AttrSpec(HasProps):
    """A container for assigning attributes to values and retrieving them as needed.

    A special function this provides is automatically handling cases where the provided
    iterator is too short compared to the distinct values provided.

    Once created as attr_spec, you can do attr_spec[data_label], where data_label must
    be a one dimensional tuple of values, representing the unique group in the data.

    See the :meth:`AttrSpec.setup` method for the primary way to provide an existing
    AttrSpec with data and column values and update all derived property values.
    """

    data = Instance(ColumnDataSource)

    iterable = List(Any, default=None)

    attrname = String(help='Name of the attribute the spec provides.')

    columns = Either(ColumnLabel, List(ColumnLabel), help="""
        The label or list of column labels that correspond to the columns that will be
        used to find all distinct values (single column) or combination of values (
        multiple columns) to then assign a unique attribute to. If not enough unique
        attribute values are found, then the attribute values will be cycled.
        """)

    default = Any(default=None, help="""
        The default value for the attribute, which is used if no column is assigned to
        the attribute for plotting. If the default value is not provided, the first
        value in the `iterable` property is used.
        """)

    attr_map = Dict(Any, Any, help="""
        Created by the attribute specification when `iterable` and `data` are
        available. The `attr_map` will include a mapping between the distinct value(s)
        found in `columns` and the attribute value that has been assigned.
        """)

    items = Any(default=None, help="""
        The attribute specification calculates this list of distinct values that are
        found in `columns` of `data`.
        """)

    sort = Bool(default=True, help="""
        A boolean flag to tell the attribute specification to sort `items`, when it is
        calculated. This affects which value of `iterable` is assigned to each distinct
        value in `items`.
        """)

    ascending = Bool(default=True, help="""
        A boolean flag to tell the attribute specification how to sort `items` if the
        `sort` property is set to `True`. The default setting for `ascending` is `True`.
        """)

    bins = Instance(Bins, help="""
        If an attribute spec is binning data, so that we can map one value in the
        `iterable` to one value in `items`, then this attribute will contain an instance
        of the Bins stat. This is used to create unique labels for each bin, which is
        then used for `items` instead of the actual unique values in `columns`.
        """)

    def __init__(self, columns=None, df=None, iterable=None, default=None,
                 items=None, **properties):
        """Create a lazy evaluated attribute specification.

        Args:
            columns: a list of column labels
            df(:class:`~pandas.DataFrame`): the data source for the attribute spec.
            iterable: an iterable of distinct attribute values
            default: a value to use as the default attribute when no columns are passed
            items: the distinct values in columns. If items is provided as input,
                then the values provided are used instead of being calculated. This can
                be used to force a specific order for assignment.
            **properties: other properties to pass to parent :class:`HasProps`
        """
        properties['columns'] = self._ensure_list(columns)

        if df is not None:
            properties['data'] = ColumnDataSource(df)

        if default is None and iterable is not None:
            default_iter = copy(iterable)
            properties['default'] = next(iter(default_iter))
        elif default is not None:
            properties['default'] = default

        if iterable is not None:
            properties['iterable'] = iterable

        if items is not None:
            properties['items'] = items

        super(AttrSpec, self).__init__(**properties)

        if self.default is None and self.iterable is not None:
            self.default = next(iter(copy(self.iterable)))

        if self.data is not None and self.columns is not None:
            if df is None:
                df = self.data.to_df()

            self._generate_items(df, columns=self.columns)

        if self.items is not None and self.iterable is not None:
            self.attr_map = self._create_attr_map()

    @staticmethod
    def _ensure_list(attr):
        """Always returns a list with the provided value. Returns the value if a list."""
        if isinstance(attr, str):
            return [attr]
        elif isinstance(attr, tuple):
            return list(attr)
        else:
            return attr

    @staticmethod
    def _ensure_tuple(attr):
        """Return tuple with the provided value. Returns the value if a tuple."""
        if not isinstance(attr, tuple):
            return (attr,)
        else:
            return attr

    def _setup_default(self):
        """Stores the first value of iterable into `default` property."""
        self.default = next(self._setup_iterable())

    def _setup_iterable(self):
        """Default behavior is to copy and cycle the provided iterable."""
        return cycle(copy(self.iterable))

    def _generate_items(self, df, columns):
        """Produce list of unique tuples that identify each item."""
        if self.sort:
            # TODO (fpliger):   this handles pandas API change so users do not experience
            #                   the related annoying deprecation warning. This is probably worth
            #                   removing when pandas deprecated version (0.16) is "old" enough
            try:
                df = df.sort_values(by=columns, ascending=self.ascending)
            except AttributeError:
                df = df.sort(columns=columns, ascending=self.ascending)

        items = df[columns].drop_duplicates()
        self.items = [tuple(x) for x in items.to_records(index=False)]

    def _create_attr_map(self, df=None, columns=None):
        """Creates map between unique values and available attributes."""

        if df is not None and columns is not None:
            self._generate_items(df, columns)

        iterable = self._setup_iterable()

        return {item: next(iterable) for item in self._item_tuples()}
    
    def _item_tuples(self):
        return [self._ensure_tuple(item) for item in self.items]

    def set_columns(self, columns):
        """Set columns property and update derived properties as needed."""
        columns = self._ensure_list(columns)

        if all([col in self.data.column_names for col in columns]):
            self.columns = columns
        else:
            # we have input values other than columns
            # assume this is now the iterable at this point
            self.iterable = columns
            self._setup_default()

    def setup(self, data=None, columns=None):
        """Set the data and update derived properties as needed."""
        if data is not None:
            self.data = data

        if columns is not None and self.data is not None:
            self.set_columns(columns)

        if self.columns is not None and self.data is not None:
            self.attr_map = self._create_attr_map(self.data.to_df(), self.columns)

    def update_data(self, data):
        self.setup(data=data, columns=self.columns)

    def __getitem__(self, item):
        """Lookup the attribute to use for the given unique group label."""

        if not self.attr_map:
            return self.default
        elif self._ensure_tuple(item) not in self.attr_map.keys():

            # make sure we have attr map
            self.setup()

        return self.attr_map[self._ensure_tuple(item)]

    @property
    def series(self):
        if not self.attr_map:
            return pd.Series()
        else:
            index = pd.MultiIndex.from_tuples(self._item_tuples(), names=self.columns)
            return pd.Series(list(self.attr_map.values()), index=index)
Esempio n. 19
0
class DataTabulator(HTMLBox):
    """A Bokeh Model that enables easy use of Tabulator tables
    See http://tabulator.info/
    """

    configuration = Dict(String, Any)

    columns = List(Instance(TableColumn),
                   help="""
    The list of child column widgets.
    """)

    download = Bool(default=False)

    editable = Bool(default=True)

    filename = String(default="table.csv")

    follow = Bool(True)

    frozen_rows = List(Int)

    groupby = List(String)

    hidden_columns = List(String)

    layout = Enum('fit_data',
                  'fit_data_fill',
                  'fit_data_stretch',
                  'fit_data_table',
                  'fit_columns',
                  default="fit_data")

    source = Instance(ColumnDataSource)

    styles = Dict(Int, Dict(Int, List(String)))

    pagination = Nullable(String)

    page = Nullable(Int)

    page_size = Int()

    max_page = Int()

    sorters = List(Dict(String, String))

    select_mode = Any(default=True)

    theme = Enum(*TABULATOR_THEMES, default="simple")

    theme_url = String(default=THEME_URL)

    __css__ = [THEME_URL + 'tabulator_simple.min.css']

    __javascript__ = [JS_SRC, MOMENT_SRC]

    __js_require__ = {
        'paths': {
            'tabulator': JS_SRC[:-3]
        },
        'exports': {
            'tabulator': 'Tabulator'
        }
    }

    __js_skip__ = {'tabulator': __javascript__}
Esempio n. 20
0
class FileInput(Widget):

    __implementation__ = os.path.join(
        os.path.abspath(os.path.dirname(__file__)), 'fileinput.ts')

    value = Any(help="Encoded file data")
Esempio n. 21
0
class DataTabulator(HTMLBox):
    """A Bokeh Model that enables easy use of Tabulator tables
    See http://tabulator.info/
    """

    aggregators = Dict(String, String)

    buttons = Dict(String, String)

    configuration = Dict(String, Any)

    columns = List(Instance(TableColumn),
                   help="""
    The list of child column widgets.
    """)

    download = Bool(default=False)

    children = Dict(Int, Instance(LayoutDOM))

    editable = Bool(default=True)

    expanded = List(Int)

    filename = String(default="table.csv")

    filters = List(Any)

    follow = Bool(True)

    frozen_rows = List(Int)

    groupby = List(String)

    hidden_columns = List(String)

    indexes = List(String)

    layout = Enum('fit_data',
                  'fit_data_fill',
                  'fit_data_stretch',
                  'fit_data_table',
                  'fit_columns',
                  default="fit_data")

    source = Instance(ColumnDataSource)

    styles = Dict(
        String,
        Either(
            String,
            Dict(Int, Dict(Int, List(Either(String, Tuple(String, String)))))))

    pagination = Nullable(String)

    page = Nullable(Int)

    page_size = Int()

    max_page = Int()

    sorters = List(Dict(String, String))

    select_mode = Any()

    selectable_rows = Nullable(List(Int))

    theme = Enum(*TABULATOR_THEMES, default="simple")

    theme_url = String(default=THEME_URL)

    __css_raw__ = CSS_URLS

    @classproperty
    def __css__(cls):
        cls.__css_raw__ = [
            url for url in cls.__css_raw__
            if 'simple' in url or len(cls.__css_raw__) == 1
        ]
        return bundled_files(cls, 'css')

    __javascript_raw__ = [JS_SRC, MOMENT_SRC]

    @classproperty
    def __javascript__(cls):
        return bundled_files(cls)

    @classproperty
    def __js_skip__(cls):
        return {
            'Tabulator': cls.__javascript__[:1],
            'moment': cls.__javascript__[1:]
        }

    __js_require__ = {
        'paths': {
            'tabulator': JS_SRC[:-3],
            'moment': MOMENT_SRC[:-3]
        },
        'exports': {
            'tabulator': 'Tabulator',
            'moment': 'moment'
        }
    }
class ChordBuilder(Builder):
    """ This is the Chord builder and it is in charge of plotting
    Chord graphs in an easy and intuitive way.

    Essentially, we provide a way to ingest the data, make the proper
    calculations and push the references into a source object.
    We additionally make calculations for the ranges. And finally add
    the needed glyphs (markers) taking the references from the source.

    """

    default_attributes = {'color': ColorAttr(),
                          'marker': MarkerAttr(),
                          'stack': CatAttr()}

    dimensions = ['values']

    values = Dimension('values')

    arcs_data = Instance(ColumnDataSource)
    text_data = Instance(ColumnDataSource)
    connection_data = Instance(ColumnDataSource)

    origin = String()
    destination = String()
    value = Any()
    square_matrix = Bool()
    label = Seq(Any())
    matrix = Array(Array(Either(Float(), Int())))

    def set_ranges(self):
        rng = 1.1 if not self.label else 1.8
        self.x_range = Range1d(-rng, rng)
        self.y_range = Range1d(-rng, rng)

    def setup(self):

        # Process only if not a square_matrix
        if not self.square_matrix:
            source = self.values._data[self.origin]
            target = self.values._data[self.destination]
            union = source.append(target).unique()
            N = union.shape[0]
            m = pd.DataFrame(np.zeros((N, N)), columns=union, index=union)

            if not self.label:
                self.label = list(union)

            if self.value is None:
                for _, row in self.values._data.iterrows():
                    m[row[self.origin]][row[self.destination]] += 1
                self.matrix = m.get_values()

            if self.value is not None:

                if isinstance(self.value, int) or isinstance(self.value, float):
                    for _, row in self.values._data.iterrows():
                        m[row[self.origin]][row[self.destination]] = self.value
                    self.matrix = m.get_values()

                elif isinstance(self.value, str):
                    for _, row in self.values._data.iterrows():
                        m[row[self.origin]][row[self.destination]] = row[self.value]
                    self.matrix = m.get_values().T
        else:
            # It's already a square matrix
            self.matrix = self._data.df.get_values()

        if self.label:
            assert len(self.label) == self.matrix.shape[0]

    def process_data(self):

        weights_of_areas = (self.matrix.sum(axis=0) + self.matrix.sum(axis=1)) - self.matrix.diagonal()
        areas_in_radians = (weights_of_areas / weights_of_areas.sum()) * (2 * pi)

        # We add a zero in the begging for the cumulative sum
        points = np.zeros((areas_in_radians.shape[0] + 1))
        points[1:] = areas_in_radians
        points = points.cumsum()

        colors = [color_in_equal_space(area / areas_in_radians.shape[0]) for area in range(areas_in_radians.shape[0])]

        arcs_data = pd.DataFrame({
            'start_angle': points[:-1],
            'end_angle': points[1:],
            'line_color': colors
        })

        self.arcs_data = ColumnDataSource(arcs_data)

        # Text
        if self.label:
            text_radius = 1.1
            angles = (points[:-1]+points[1:])/2.0

            text_positions = pd.DataFrame({
                'angles': angles,
                'text_x': np.cos(angles) * text_radius,
                'text_y': np.sin(angles) * text_radius,
                'text': list(self.label)
            })

            self.text_data = ColumnDataSource(text_positions)

        # Lines

        all_areas = []
        for i in range(areas_in_radians.shape[0]):
            all_areas.append(Area(weights_of_areas[i], points[:-1][i], points[1:][i]))

        all_connections = []
        for j, region1 in enumerate(self.matrix):
            # Get the connections origin region
            source = all_areas[j]
            color = colors[j]
            weight = weights_of_areas[j]

            for k, region2 in enumerate(region1):
                # Get the connection destination region
                target = all_areas[k]
                for _ in range(int(region2)):
                    p1 = source.free_points.pop()
                    p2 = target.free_points.pop()
                    # Get both regions free points and create a connection with the data
                    all_connections.append(p1 + p2 + [color, weight])

        connections_df = pd.DataFrame(all_connections, dtype=str)
        connections_df.columns = ["start_x", "start_y", "end_x", "end_y", "colors", "weight"]
        connections_df["cx0"] = connections_df.start_x.astype("float64")/2
        connections_df["cy0"] = connections_df.start_y.astype("float64")/2
        connections_df["cx1"] = connections_df.end_x.astype("float64")/2
        connections_df["cy1"] = connections_df.end_y.astype("float64")/2
        connections_df.weight = (connections_df.weight.astype("float64")/connections_df.weight.astype("float64").sum()) * 3000

        self.connection_data = ColumnDataSource(connections_df)

    def yield_renderers(self):
        """Use the marker glyphs to display the arcs and beziers.
        Takes reference points from data loaded at the ColumnDataSource.
        """
        beziers = Bezier(x0='start_x',
                         y0='start_y',
                         x1='end_x',
                         y1='end_y',
                         cx0='cx0',
                         cy0='cy0',
                         cx1='cx1',
                         cy1='cy1',
                         line_alpha='weight',
                         line_color='colors')

        yield GlyphRenderer(data_source=self.connection_data, glyph=beziers)

        arcs = Arc(x=0,
                   y=0,
                   radius=1,
                   line_width=10,
                   start_angle='start_angle',
                   end_angle='end_angle',
                   line_color='line_color')

        yield GlyphRenderer(data_source=self.arcs_data, glyph=arcs)

        if self.label:

            text_props = {
                "text_color": "#000000",
                "text_font_size": "8pt",
                "text_align": "left",
                "text_baseline": "middle"
            }

            labels = Text(x='text_x',
                          y='text_y',
                          text='text',
                          angle='angles',
                          **text_props
                          )

            yield GlyphRenderer(data_source=self.text_data, glyph=labels)
Esempio n. 23
0
class CompositeGlyph(HasProps):
    """Represents a subset of data.

    A collection of hetero or homogeneous glyph
    renderers which represent a subset of data. The
    purpose of the composite glyph is to abstract
    away the details of constructing glyphs, based on
    the details of a subset of data, from the grouping
    operations that a generalized builders must implement.

    In general, the Builder operates at the full column
    oriented data source level, segmenting and assigning
    attributes from a large selection, while the composite glyphs
    will typically be passed an array-like structures with
    one or more singular attributes to apply.

    Another way to explain the concept is that the Builder
    operates as the groupby, as in pandas, while the
    CompositeGlyph operates as the function used in the apply.

    What is the responsibility of the Composite Glyph?
        - Produce GlyphRenderers
        - Apply any aggregations
        - Tag the GlyphRenderers with the group label
        - Apply transforms due to chart operations
            - Note: Operations require implementation of special methods
    """

    # composite glyph inputs
    data = Any()
    label = Either(String,
                   Dict(String, Any),
                   default='None',
                   help='Identifies the subset of data.')

    values = Either(Column(Float),
                    Column(String),
                    help="""Array-like values,
        which are used as the input to the composite glyph.""")

    # derived from inputs
    source = Instance(ColumnDataSource,
                      help="""The data source used for the contained
        glyph renderers. Simple glyphs part of the composite glyph might not use the
        column data source.""")
    renderers = List(Instance(GlyphRenderer))
    glyphs = Dict(String, Instance(Glyph))

    operations = List(Any,
                      help="""A list of chart operations that can be applied to
        manipulate their visual depiction.""")

    color = Color(default='gray',
                  help="""A high level color. Some glyphs will
        implement more specific color attributes for parts or specific glyphs."""
                  )
    fill_color = Color(default="gray")
    line_color = Color(default='black',
                       help="""A default outline color for contained
        glyphs.""")
    fill_alpha = Float(default=0.8)
    line_alpha = Float(default=1.0)

    left_buffer = Float(default=0.0)
    right_buffer = Float(default=0.0)
    top_buffer = Float(default=0.0)
    bottom_buffer = Float(default=0.0)

    def setup(self):
        """Build renderers and data source and set sources on renderers."""
        self.renderers = [renderer for renderer in self.build_renderers()]
        if self.renderers is not None:
            self.refresh()

    def refresh(self):
        """Update the GlyphRenderers.

        .. note:
            this method would be called after data is added.
        """
        if self.renderers is not None:
            data = self.build_source()

            if data is not None:

                if isinstance(data, dict):
                    source = ColumnDataSource(data)

                if not isinstance(source,
                                  ColumnDataSource) and source is not None:
                    raise TypeError(
                        'build_source must return dict or ColumnDataSource.')
                else:
                    self.source = self.add_chart_index(source)

                self._set_sources()

    def add_chart_index(self, data):

        if isinstance(data, ColumnDataSource):
            source = data
            data = source.data
        else:
            source = None

        # add chart index to data
        if 'chart_index' not in data:
            n_rows = len(list(data.values())[0])

            # add composite chart index as column
            data['chart_index'] = [self.label] * n_rows

            # add constant value for each column in chart index
            if isinstance(self.label, dict):
                for col, val in iteritems(self.label):
                    data[col] = [val] * n_rows

        if source is not None:
            source.data = data
            return source
        else:
            return data

    def build_renderers(self):
        raise NotImplementedError('You must return list of renderers.')

    def build_source(self):
        raise NotImplementedError('You must return ColumnDataSource.')

    def _set_sources(self):
        """Store reference to source in each GlyphRenderer.

        .. note::
            if the glyphs that are part of the composite glyph differ, you may have to
            override this method and handle the sources manually.
        """
        for renderer in self.renderers:
            renderer.data_source = self.source

    def __stack__(self, glyphs):
        """A special method the `stack` function applies to composite glyphs."""
        pass

    def __jitter__(self, glyphs):
        """A special method the `jitter` function applies to composite glyphs."""
        pass

    def __dodge__(self, glyphs):
        """A special method the `dodge` function applies to composite glyphs."""
        pass

    def __overlay__(self, glyphs):
        """A special method the `overlay` function applies to composite glyphs."""
        pass

    def apply_operations(self):
        pass

    @classmethod
    def glyph_properties(cls):
        props = {}
        for name, glyph in iteritems(cls.glyphs):
            props[name] = glyph.class_properties(withbases=True)

        return props