from typing import Dict, List, Optional, Tuple, Type, Union
from shyft.dashboard.time_series.state import Unit
from shyft.dashboard.base.hashable import Hashable
from shyft.dashboard.time_series.bindable import Bindable
from shyft.dashboard.time_series.renderer import (BaseFigureRenderer, FillInBetweenRenderer, LineRenderer,
CircleScatterRenderer, SquareScatterRenderer, TriangleScatterRenderer,
VALID_LINE_STYLES, DiamondScatterRenderer, BackgroundDataRenderer,
MultiLineRenderer)
from shyft.dashboard.time_series.attr_callback_manager import AttributeCallbackManager
from shyft.dashboard.time_series.axes import YAxis
base_view_container = 'shyft.dashboard.time_series.view_container.view_container_base.BaseViewContainer'
figure_container = 'shyft.dashboard.time_series.view_container.figure.Figure'
legend_container = 'shyft.dashboard.time_series.view_container.legend.Legend'
table_container = 'shyft.dashboard.time_series.view_container.table.Table'
[docs]
class ViewError(RuntimeError):
pass
[docs]
class BaseView(Hashable, Bindable, AttributeCallbackManager):
"""
This object represents the immutable, hashable view base which can be used as dict keys
"""
[docs]
def __init__(self, *, view_container: base_view_container, label: str, visible: bool = True,
tooltips: Optional[List[Tuple[str, str]]] = None) -> None:
"""
Base class of all views, makes no sense to use it on its own.
Parameters
----------
view_container: view_container
label: label of the view
visible: visibility of the view
"""
AttributeCallbackManager.__init__(self)
Hashable.__init__(self)
Bindable.__init__(self)
self.__view_container = view_container
self.label = label
self.visible = visible
self.tooltips = tooltips
@property
def view_container(self) -> base_view_container:
return self.__view_container
[docs]
class LegendItem(BaseView):
"""
This object defines the data legend view
"""
[docs]
def __init__(self, *, view_container: legend_container, label: str, views: List[BaseView], expanded: Optional[bool] = None) -> None:
"""
Legend Item to show in the given legend: view_container
Parameters
----------
view_container: Legend instance where to add this legend item
label: label in the legend
views: all views which are represented by this legend item (will be used to trigger visibility)
Notes
-----
One view can be in several LegendItem s but this might lead to strange behaviour
"""
if not label:
label = ', '.join([v.label for v in views])
super().__init__(view_container=view_container, label=label, visible=True)
self.views = views
self.expanded = expanded
[docs]
class TableView(BaseView):
"""
This object defines the data for table view
"""
[docs]
def __init__(self, *, view_container: table_container, label: str, unit: Unit, columns: Dict[int, str] = None,
visible: bool = True) -> None:
"""
View defining one or more colums in the provided table, view_container,
Parameters
----------
view_container:
Figure view container where to plot the renderer of the view
label:
main label of the view
unit:
unit of the data in the columns
visible:
controls the visibility of all columns
columns:
Dict[index:int, column_label:str] defines all columns a la:
index of the time series in the ts_vector to add
column_label of the specific column
Notes
-----
The following values can be changed from the outside during runtime:
- visible
- unit
Final column labels are combined from label + column_label.
"""
super().__init__(view_container=view_container, label=label, visible=visible)
self.columns = columns or {}
self.unit = unit
[docs]
class Line(FigureView):
"""
This object contains all data for line view
"""
[docs]
def __init__(self, *, view_container: figure_container, color: str, label: str, unit: Unit,
index: int, no_y_rescaling: Optional[bool] = False, visible: bool = True,
y_axis: Optional[YAxis] = None, line_style: str = 'solid',
line_width: float = 2., tooltips: List[Tuple[str, str]] = None):
"""
Plots a line for a time series with the index in the ts-vector from the data source
Parameters
----------
view_container: Figure view container where to plot the renderer of the view
color: color of the line
label: label of the view
unit: unit of the data in the renderer
index: index defining the time series in the ts-vector
no_y_rescaling: If data should be used for y axis rescaling
visible: controls the visiblity of the renderer
y_axis: defines on which YAxis the renderer should be plotted
line_width: line width of the line
Notes
-----
The following values can be changed from the outside during runtime:
color, label, unit, indices, visible
"""
tooltips = tooltips or [("label", "@label")]
super().__init__(view_container=view_container, color=color, label=label, unit=unit,
no_y_rescaling=no_y_rescaling, visible=visible, renderer_class=LineRenderer,
y_axis=y_axis, tooltips=tooltips)
self.index = index
if line_style not in VALID_LINE_STYLES:
raise ViewError(f'view {label} in {view_container}: line_style {line_style} not in {VALID_LINE_STYLES}')
self.line_style = line_style
self.line_width = line_width
@property
def indices(self) -> List[int]:
"""Enables using common calls to Line and MultiLine. Should be refactored eventually."""
return [self.index]
[docs]
class Scatter(FigureView):
"""
This object contains all data for a scatter view
"""
[docs]
def __init__(self, *, view_container: figure_container, color: str, label: str, unit: Unit,
index: int, renderer_class: Union[Type[DiamondScatterRenderer],
Type[CircleScatterRenderer],
Type[SquareScatterRenderer],
Type[TriangleScatterRenderer]],
no_y_rescaling: Optional[bool] = False, visible: bool = True, size: int = 4,
fill_color: str = None, fill_alpha: float = 1., line_alpha: float = 1.,
y_axis: Optional[YAxis] = None, tooltips: List[Tuple[str, str]] = None):
"""
Plots a line for a time series with the index in the ts-vector from the data source
Parameters
----------
view_container:
Figure view container where to plot the renderer of the view
color:
color of the line
renderer_class:
defines which scatter it is going to be
fill_color:
fill_color of the scatter
fill_alpha:
alpha transparency of the scatter fill_color
line_alpha:
alpha transparency of the scatter line
label:
label of the view
unit:
unit of the data in the renderer
index:
index defining the time series in the ts-vector
no_y_rescaling:
If data should be used for y axis rescaling
visible:
controls the visibility of the renderer
y_axis:
defines on which YAxis the renderer should be plotted
size:
defines the size of the scatter
Notes
-----
The following values can be changed from the outside during runtime:
color, label, unit, indices, visible, size, fill_color, fill_alpha, line_alpha
"""
expected_renderer_class = [DiamondScatterRenderer, CircleScatterRenderer, SquareScatterRenderer,
TriangleScatterRenderer]
if renderer_class not in expected_renderer_class:
raise ValueError(f"Scatter: expected renderer class should be one of {expected_renderer_class}, "
f"got {renderer_class}")
super().__init__(view_container=view_container, color=color, label=label, unit=unit,
no_y_rescaling=no_y_rescaling, visible=visible, renderer_class=renderer_class,
y_axis=y_axis, tooltips=tooltips)
self.index = index
self.line_style = ""
self.size = size
self.fill_color = fill_color
self.fill_alpha = fill_alpha
self.line_alpha = line_alpha
@property
def indices(self) -> List[int]:
"""Enables using common calls as MultiLine. Should be refactored eventually."""
return [self.index]
[docs]
class DiamondScatter(Scatter):
"""
This object contains all data for diamond view
"""
[docs]
def __init__(self, *, view_container: figure_container, color: str, label: str, unit: Unit,
index: int, no_y_rescaling: Optional[bool] = False, visible: bool = True, size: int = 4,
fill_color: str = None, fill_alpha: float = 1., line_alpha: float = 1.,
y_axis: Optional[YAxis] = None, tooltips: List[Tuple[str, str]] = None):
"""
Plots a line for a time series with the index in the ts-vector from the data source
Parameters
----------
view_container:
Figure view container where to plot the renderer of the view
color:
color of the line
fill_color:
fill_color of the scatter
fill_alpha:
alpha transparency of the scatter fill_color
line_alpha:
alpha transparency of the scatter line
label:
label of the view
unit:
unit of the data in the renderer
index:
index defining the time series in the ts-vector
no_y_rescaling:
If data should be used for y axis rescaling
visible:
controls the visibility of the renderer
y_axis:
defines on which YAxis the renderer should be plotted
size:
defines the size of the scatter
Notes
-----
The following values can be changed from the outside during runtime:
color, label, unit, indices, visible, size, fill_color, fill_alpha, line_alpha
"""
super().__init__(view_container=view_container, color=color, label=label, unit=unit,
index=index, size=size, fill_color=fill_color, fill_alpha=fill_alpha, line_alpha=line_alpha,
no_y_rescaling=no_y_rescaling, visible=visible, renderer_class=DiamondScatterRenderer,
y_axis=y_axis, tooltips=tooltips)
[docs]
class CircleScatter(Scatter):
"""
This object contains all data for diamond view
"""
[docs]
def __init__(self, *, view_container: figure_container, color: str, label: str, unit: Unit,
index: int, no_y_rescaling: Optional[bool] = False, visible: bool = True, size: int = 4,
fill_color: str = None, fill_alpha: float = 1., line_alpha: float = 1.,
y_axis: Optional[YAxis] = None, tooltips: List[Tuple[str, str]] = None):
"""
Plots a line for a time series with the index in the ts-vector from the data source
Parameters
----------
view_container:
Figure view container where to plot the renderer of the view
color:
color of the line
fill_color:
fill_color of the scatter
fill_alpha:
alpha transparency of the scatter fill_color
line_alpha:
alpha transparency of the scatter line
label:
label of the view
unit:
unit of the data in the renderer
index:
index defining the time series in the ts-vector
no_y_rescaling:
If data should be used for y axis rescaling
visible:
controls the visibility of the renderer
y_axis:
defines on which YAxis the renderer should be plotted
size:
defines the size of the scatter
Notes
-----
The following values can be changed from the outside during runtime:
color, label, unit, indices, visible, size, fill_color, fill_alpha, line_alpha
"""
super().__init__(view_container=view_container, color=color, label=label, unit=unit,
index=index, size=size, fill_color=fill_color, fill_alpha=fill_alpha, line_alpha=line_alpha,
no_y_rescaling=no_y_rescaling, visible=visible, renderer_class=CircleScatterRenderer,
y_axis=y_axis, tooltips=tooltips)
[docs]
class SquareScatter(Scatter):
"""
This object contains all data for diamond view
"""
[docs]
def __init__(self, *, view_container: figure_container, color: str, label: str, unit: Unit,
index: int, no_y_rescaling: Optional[bool] = False, visible: bool = True, size: int = 4,
fill_color: str = None, fill_alpha: float = 1., line_alpha: float = 1.,
y_axis: Optional[YAxis] = None, tooltips: List[Tuple[str, str]] = None):
"""
Plots a line for a time series with the index in the ts-vector from the data source
Parameters
----------
view_container:
Figure view container where to plot the renderer of the view
color:
color of the line
fill_color:
fill_color of the scatter
fill_alpha:
alpha transparency of the scatter fill_color
line_alpha:
alpha transparency of the scatter line
label:
label of the view
unit:
unit of the data in the renderer
index:
index defining the time series in the ts-vector
no_y_rescaling:
If data should be used for y axis rescaling
visible:
controls the visibility of the renderer
y_axis:
defines on which YAxis the renderer should be plotted
size:
defines the size of the scatter
Notes
-----
The following values can be changed from the outside during runtime:
color, label, unit, indices, visible, size, fill_color, fill_alpha, line_alpha
"""
super().__init__(view_container=view_container, color=color, label=label, unit=unit,
index=index, size=size, fill_color=fill_color, fill_alpha=fill_alpha, line_alpha=line_alpha,
no_y_rescaling=no_y_rescaling, visible=visible, renderer_class=SquareScatterRenderer,
y_axis=y_axis, tooltips=tooltips)
[docs]
class TriangleScatter(Scatter):
"""
This object contains all data for diamond view
"""
[docs]
def __init__(self, *, view_container: figure_container, color: str, label: str, unit: Unit,
index: int, no_y_rescaling: Optional[bool] = False, visible: bool = True, size: int = 4,
fill_color: str = None, fill_alpha: float = 1., line_alpha: float = 1.,
y_axis: Optional[YAxis] = None, tooltips: List[Tuple[str, str]] = None):
"""
Plots a line for a time series with the index in the ts-vector from the data source
Parameters
----------
view_container:
Figure view container where to plot the renderer of the view
color:
color of the line
fill_color:
fill_color of the scatter
fill_alpha:
alpha transparency of the scatter fill_color
line_alpha:
alpha transparency of the scatter line
label:
label of the view
unit:
unit of the data in the renderer
index:
index defining the time series in the ts-vector
no_y_rescaling:
If data should be used for y axis rescaling
visible:
controls the visibility of the renderer
y_axis:
defines on which YAxis the renderer should be plotted
size:
defines the size of the scatter
Notes
-----
The following values can be changed from the outside during runtime:
color, label, unit, indices, visible, size, fill_color, fill_alpha, line_alpha
"""
super().__init__(view_container=view_container, color=color, label=label, unit=unit,
index=index, size=size, fill_color=fill_color, fill_alpha=fill_alpha, line_alpha=line_alpha,
no_y_rescaling=no_y_rescaling, visible=visible, renderer_class=TriangleScatterRenderer,
y_axis=y_axis, tooltips=tooltips)
[docs]
class FillInBetween(FigureView):
"""
This object contains all data for fill in between view
"""
[docs]
def __init__(self, *, view_container: figure_container, color: str, label: str, unit: Unit,
indices: Tuple[int, int], no_y_rescaling: Optional[bool] = False, visible: bool = True,
y_axis: Optional[YAxis] = None, fill_alpha: float = 0.5,
tooltips: List[Tuple[str, str]] = None):
"""
Plots a filled patch between the data of two time series in the ts-vector from the data source
Parameters
----------
view_container: Figure view container where to plot the renderer of the view
color: Color of the filling
fill_alpha: defines the alpha value of the filling default is 0.5
label: label of the view
unit: unit of the data in the renderer
indices: tuple with 2 indices defining the 2 time series in the ts-vector
no_y_rescaling: If data should be used for y axis rescaling
visible: controls the visiblity of the renderer
y_axis: defines on which YAxis the renderer should be plotted
Notes
-----
The following values can be changed from the outside during runtime:
color, fill_alpha, label, unit, indices, visible
"""
if tooltips is None:
tooltips = [("label", "@label")]
super().__init__(view_container=view_container, color=color, label=label, unit=unit,
no_y_rescaling=no_y_rescaling, visible=visible, renderer_class=FillInBetweenRenderer,
y_axis=y_axis, tooltips=tooltips)
self.indices = indices
self.fill_alpha = fill_alpha
# self.tooltip = [("Name", "@label"), ("temperatures", ToolTipFunction.average), ("pos", "12312,332, 1232.333")]
[docs]
class MultiLine(FigureView):
"""
This object contains all data for a multiple-line view.
"""
[docs]
def __init__(self, *, view_container: figure_container, colors: List[str], labels: List[str], unit: Unit,
indices: List[int] = None, no_y_rescaling: Optional[bool] = False, visible: bool = True,
y_axis: Optional[YAxis] = None, line_styles: List[str] = None, line_widths: List[float] = None,
expandable: Optional[bool] = False,
tooltips: List[Tuple[str, str]] = None):
"""
Plots multiple lines for a time series with selected scenarios in the ts-vector from the data source
Parameters
----------
view_container: Figure view container where to plot the renderer of the view
colors: color of the line
labels: label of the view
unit: unit of the data in the renderer
indices: a list of indices defining the time series in the ts-vector
no_y_rescaling: If data should be used for y axis rescaling
visible: controls the visibility of the renderer
y_axis: defines on which YAxis the renderer should be plotted
expandable:
Notes
-----
The following values can be changed from the outside during runtime:
color, label, unit, indices, visible
"""
if tooltips is None:
tooltips = [("label", "@label")]
if line_styles is None:
line_styles = ['solid']
if line_widths is None:
line_widths = [2.0]
if indices is None:
indices = []
super().__init__(view_container=view_container, color=colors, label=labels, unit=unit,
no_y_rescaling=no_y_rescaling, visible=visible, renderer_class=MultiLineRenderer,
y_axis=y_axis, tooltips=tooltips)
self.indices = indices
self.labels = labels
self.expanded = True
self.expandable = expandable
for line_style in line_styles:
if line_style not in VALID_LINE_STYLES:
raise ViewError(
f'view {labels} in {view_container}: line_style {line_style} not in {VALID_LINE_STYLES}')
self.line_styles = line_styles
self.line_widths = line_widths
[docs]
class BackgroundData(FigureView):
"""
This object contains all data for background data patches
"""
[docs]
def __init__(self, *, view_container: figure_container,
label: str,
unit: Unit,
values_color_map=Dict[int, str],
default_color: str = 'white',
index: int, visible: bool = True,
y_axis: Optional[YAxis] = None,
fill_alpha: float = 0.5,
y_max: int = 1000,
y_min: int = -1000,
show_not_defined: bool = False):
"""
Plots the data of a time series in the ts-vector from the data source as filled background patches.
Values of the time series are mapped to colors using the values_color_map.
For now only a single value to color are supported. See also example.
Parameters
----------
view_container:
Figure view container where to plot the renderer of the view
fill_alpha:
defines the alpha value of the filling default is 0.5
label:
label of the view
unit:
unit of the data in the renderer
index:
index defining the time series in the ts-vector from data source to plot
default_color:
The color everything not defined in color map should have, has only an effect if show_not_defined=True
show_not_defined:
Show values not defined in the value_color_map with default_color
values_color_map:
Defines the mapping from value to color to use
visible:
controls the visibility of the renderer
y_axis:
defines on which YAxis the renderer should be plotted
y_max:
value to use as max value for the y axis
y_min:
value to use as min value for the y axis
color, label, unit, index, visible, y_max, y_min, values_color_map, fill_alpha, show_not_defined
"""
super().__init__(view_container=view_container, color=default_color, label=label, unit=unit,
no_y_rescaling=True, visible=visible, renderer_class=BackgroundDataRenderer,
y_axis=y_axis)
# validate value_color_map:
for k, v in values_color_map.items():
if not isinstance(v, dict):
raise ValueError(
f"BackgroundData in values_color_map: expect values in "
f"form of Dict[str, str] with keys `label` and `color` got {v}")
for label in ['color', 'label']:
if label not in v:
raise KeyError(f"BackgroundData in values_color_map: `{label}` not in dict of {k}: got {v}")
if not isinstance(v[label], str):
raise ValueError(f"BackgroundData in values_color_map: `{label}` not of type str: got {v[label]} {type(v[label])}")
self.index = index
self.fill_alpha = fill_alpha
self.values_color_map = values_color_map
self.y_max = y_max
self.y_min = y_min
self.show_not_defined = show_not_defined