-
-
Notifications
You must be signed in to change notification settings - Fork 394
/
tabular.py
128 lines (106 loc) · 4.83 KB
/
tabular.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import param
from bokeh.models.widgets import (
DataTable, TableColumn, NumberEditor, NumberFormatter, DateFormatter,
TimeEditor, StringFormatter, StringEditor, IntEditor
)
from ...core import Dataset, Dimension
from ...element import ItemTable
from ...streams import Buffer
from ...core.util import dimension_sanitizer, datetime_types
from ..plot import GenericElementPlot
from .plot import BokehPlot
class TablePlot(BokehPlot, GenericElementPlot):
height = param.Number(default=None)
width = param.Number(default=400)
style_opts = ['row_headers', 'selectable', 'editable',
'sortable', 'fit_columns', 'scroll_to_selection']
finalize_hooks = param.HookList(default=[], doc="""
Optional list of hooks called when finalizing a column.
The hook is passed the plot object and the displayed
object, and other plotting handles can be accessed via plot.handles.""")
_stream_data = True
def __init__(self, element, plot=None, **params):
super(TablePlot, self).__init__(element, **params)
self.handles = {} if plot is None else self.handles['plot']
element_ids = self.hmap.traverse(lambda x: id(x), [Dataset, ItemTable])
self.static = len(set(element_ids)) == 1 and len(self.keys) == len(self.hmap)
self.callbacks = self._construct_callbacks()
self.streaming = [s for s in self.streams if isinstance(s, Buffer)]
self.static_source = False
def _execute_hooks(self, element):
"""
Executes finalize hooks
"""
for hook in self.finalize_hooks:
try:
hook(self, element)
except Exception as e:
self.warning("Plotting hook %r could not be applied:\n\n %s" % (hook, e))
def get_data(self, element, ranges, style):
return ({dimension_sanitizer(d.name): element.dimension_values(d)
for d in element.dimensions()}, {}, style)
def initialize_plot(self, ranges=None, plot=None, plots=None, source=None):
"""
Initializes a new plot object with the last available frame.
"""
# Get element key and ranges for frame
element = self.hmap.last
key = self.keys[-1]
self.current_frame = element
self.current_key = key
style = self.lookup_options(element, 'style')[self.cyclic_index]
data, _, style = self.get_data(element, ranges, style)
if source is None:
source = self._init_datasource(data)
self.handles['source'] = source
columns = []
dims = element.dimensions()
for d in dims:
col = dimension_sanitizer(d.name)
kind = data[col].dtype.kind
if kind == 'i':
formatter = NumberFormatter()
editor = IntEditor()
elif kind == 'f':
formatter = NumberFormatter(format='0,0.0[00000]')
editor = NumberEditor()
elif kind == 'M' or (kind == 'O' and type(data[col][0]) in datetime_types):
dimtype = element.get_dimension_type(0)
dformat = Dimension.type_formatters.get(dimtype, '%Y-%m-%d %H:%M:%S')
formatter = DateFormatter(format=dformat)
editor = TimeEditor()
else:
formatter = StringFormatter()
editor = StringEditor()
column = TableColumn(field=d.name, title=d.pprint_label,
editor=editor, formatter=formatter)
columns.append(column)
style['reorderable'] = False
table = DataTable(source=source, columns=columns, height=self.height,
width=self.width, **style)
self.handles['plot'] = table
self.handles['glyph_renderer'] = table
self._execute_hooks(element)
self.drawn = True
for cb in self.callbacks:
cb.initialize()
return table
def update_frame(self, key, ranges=None, plot=None):
"""
Updates an existing plot with data corresponding
to the key.
"""
element = self._get_frame(key)
# Cache frame object id to skip updating data if unchanged
previous_id = self.handles.get('previous_id', None)
current_id = element._plot_id
self.handles['previous_id'] = current_id
self.static_source = (self.dynamic and (current_id == previous_id))
if (element is None or (not self.dynamic and self.static) or
(self.streaming and self.streaming[0].data is self.current_frame.data
and not self.streaming[0]._triggering) or self.static_source):
return
source = self.handles['source']
style = self.lookup_options(element, 'style')[self.cyclic_index]
data, _, style = self.get_data(element, ranges, style)
self._update_datasource(source, data)