/
tabular.py
150 lines (129 loc) · 5.67 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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import param
from bokeh.models import Column
from bokeh.models.widgets import (
DataTable,
DateEditor,
DateFormatter,
IntEditor,
NumberEditor,
NumberFormatter,
StringEditor,
StringFormatter,
TableColumn,
)
from ...core import Dataset, Dimension
from ...core.util import dimension_sanitizer, isdatetime
from ...element import ItemTable
from ...streams import Buffer
from ..plot import GenericElementPlot
from .plot import BokehPlot
from .selection import TabularSelectionDisplay
class TablePlot(BokehPlot, GenericElementPlot):
hooks = param.HookList(default=[], doc="""
Optional list of hooks called when finalizing a plot. The
hook is passed the plot object and the displayed element, and
other plotting handles can be accessed via plot.handles.""")
height = param.Number(default=300)
selected = param.List(default=None, doc="""
The current selection as a list of integers corresponding
to the selected items.""")
width = param.Number(default=400)
selection_display = TabularSelectionDisplay()
style_opts = ['row_headers', 'selectable', 'editable',
'sortable', 'fit_columns', 'scroll_to_selection',
'index_position', 'visible']
_stream_data = True
def __init__(self, element, plot=None, **params):
super().__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.source_streams = self._construct_callbacks()
self.streaming = [s for s in self.streams if isinstance(s, Buffer)]
self.static_source = False
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'] = self.handles['cds'] = source
self.handles['selected'] = source.selected
if self.selected is not None:
source.selected.indices = self.selected
columns = self._get_columns(element, data)
style['reorderable'] = False
table = DataTable(source=source, columns=columns, height=self.height,
width=self.width, **style)
self.handles['table'] = table
self.handles['glyph_renderer'] = table
self._execute_hooks(element)
self.drawn = True
title = self._get_title_div(self.keys[-1], '10pt')
if title:
plot = Column(title, table)
self.handles['title'] = title
else:
plot = table
self.handles['plot'] = plot
for cb in self.callbacks:
cb.initialize()
return plot
def _get_columns(self, element, data):
columns = []
for d in element.dimensions():
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 isdatetime(data[col]):
dimtype = element.get_dimension_type(col)
dformat = Dimension.type_formatters.get(dimtype, '%Y-%m-%d %H:%M:%S')
formatter = DateFormatter(format=dformat)
editor = DateEditor()
else:
formatter = StringFormatter()
editor = StringEditor()
column = TableColumn(field=dimension_sanitizer(d.name), title=d.pprint_label,
editor=editor, formatter=formatter)
columns.append(column)
return columns
def update_frame(self, key, ranges=None, plot=None):
"""
Updates an existing plot with data corresponding
to the key.
"""
element = self._get_frame(key)
self.param.update(**self.lookup_options(element, 'plot').options)
self._get_title_div(key, '12pt')
# Cache frame object id to skip updating data if unchanged
previous_id = self.handles.get('previous_id', None)
current_id = element._plot_id
source = self.handles['source']
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):
if self.static_source and hasattr(self, 'selected') and self.selected is not None:
self._update_selected(source)
return
style = self.lookup_options(element, 'style')[self.cyclic_index]
data, _, style = self.get_data(element, ranges, style)
columns = self._get_columns(element, data)
self.handles['table'].columns = columns
self._update_datasource(source, data)