|
|
|
@ -33,86 +33,50 @@ class Bar(Graph):
|
|
|
|
|
self._x_ranges = None |
|
|
|
|
super(Bar, self).__init__(*args, **kwargs) |
|
|
|
|
|
|
|
|
|
def bar(self, serie_node, serie, values, index, stack_vals=None): |
|
|
|
|
def _bar(self, parent, x, y, index): |
|
|
|
|
width = self.view.x(1 / self._len) |
|
|
|
|
margin = width * .1 * 0 |
|
|
|
|
x += margin |
|
|
|
|
width -= 2 * margin |
|
|
|
|
width /= self._order |
|
|
|
|
x += index * width |
|
|
|
|
padding = width * .005 * 0 |
|
|
|
|
x += padding |
|
|
|
|
width -= 2 * padding |
|
|
|
|
|
|
|
|
|
y_0 = self.view.y(self.zero) |
|
|
|
|
height = abs(y - y_0) |
|
|
|
|
self.svg.transposable_node( |
|
|
|
|
parent, |
|
|
|
|
'rect', |
|
|
|
|
x=x, |
|
|
|
|
y=y, |
|
|
|
|
rx=self.rounded_bars * 1 if self.rounded_bars else 0, |
|
|
|
|
ry=self.rounded_bars * 1 if self.rounded_bars else 0, |
|
|
|
|
width=width, |
|
|
|
|
height=height, |
|
|
|
|
class_='rect reactive tooltip-trigger') |
|
|
|
|
return (x + width / 2, y + height / 2) |
|
|
|
|
|
|
|
|
|
def bar(self, serie_node, serie, index): |
|
|
|
|
"""Draw a bar graph for a serie""" |
|
|
|
|
# value here is a list of tuple range of tuple coord |
|
|
|
|
|
|
|
|
|
def view(rng): |
|
|
|
|
"""Project range""" |
|
|
|
|
t, T = rng |
|
|
|
|
fun = swap if self.horizontal else ident |
|
|
|
|
return self.view(fun(t)), self.view(fun(T)) |
|
|
|
|
|
|
|
|
|
bars = self.svg.node(serie_node['plot'], class_="bars") |
|
|
|
|
view_values = map(view, values) |
|
|
|
|
for i, ((x, y), (X, Y)) in enumerate(view_values): |
|
|
|
|
view_values = map(self.view, serie.points) |
|
|
|
|
for i, (x, y) in enumerate(view_values): |
|
|
|
|
if None in (x, y): |
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
# +-------+ |
|
|
|
|
# | | |
|
|
|
|
# | | |
|
|
|
|
# | +-------+ |
|
|
|
|
# | | | |
|
|
|
|
# | | | |
|
|
|
|
# | | | |
|
|
|
|
# +-------+-------+ |
|
|
|
|
# (x,y) (X,Y) |
|
|
|
|
# |
|
|
|
|
# x and y are left range coords and X, Y right ones |
|
|
|
|
metadata = serie.metadata.get(i) |
|
|
|
|
val = self._format(values[i][1][1]) |
|
|
|
|
if self.horizontal: |
|
|
|
|
x, y, X, Y = Y, X, y, x |
|
|
|
|
width = X - x |
|
|
|
|
padding = .1 * width |
|
|
|
|
inner_width = width - 2 * padding |
|
|
|
|
if self.horizontal: |
|
|
|
|
height = self.view.x(self.zero) - y |
|
|
|
|
else: |
|
|
|
|
height = self.view.y(self.zero) - y |
|
|
|
|
if stack_vals is None: |
|
|
|
|
bar_width = inner_width / self._order |
|
|
|
|
bar_padding = .1 * bar_width |
|
|
|
|
bar_inner_width = bar_width - 2 * bar_padding |
|
|
|
|
offset = index * bar_width + bar_padding |
|
|
|
|
shift = 0 |
|
|
|
|
else: |
|
|
|
|
offset = 0 |
|
|
|
|
bar_inner_width = inner_width |
|
|
|
|
shift = stack_vals[i][int(height < 0)] |
|
|
|
|
stack_vals[i][int(height < 0)] += height |
|
|
|
|
x = x + padding + offset |
|
|
|
|
|
|
|
|
|
if height < 0: |
|
|
|
|
y = y + height |
|
|
|
|
height = -height |
|
|
|
|
y -= shift |
|
|
|
|
|
|
|
|
|
bar = decorate( |
|
|
|
|
self.svg, |
|
|
|
|
self.svg.node(bars, class_='bar'), |
|
|
|
|
metadata) |
|
|
|
|
self.svg.transposable_node( |
|
|
|
|
bar, 'rect', |
|
|
|
|
x=x, |
|
|
|
|
y=y, |
|
|
|
|
rx=self.rounded_bars * 1 if self.rounded_bars else 0, |
|
|
|
|
ry=self.rounded_bars * 1 if self.rounded_bars else 0, |
|
|
|
|
width=bar_inner_width, |
|
|
|
|
height=height, |
|
|
|
|
class_='rect reactive tooltip-trigger') |
|
|
|
|
|
|
|
|
|
x += bar_inner_width / 2 |
|
|
|
|
y += height / 2 |
|
|
|
|
val = self._get_value(serie.points, i) |
|
|
|
|
|
|
|
|
|
if self.horizontal: |
|
|
|
|
x, y = y, x |
|
|
|
|
|
|
|
|
|
self._tooltip_data(bar, val, x, y, classes="centered") |
|
|
|
|
self._static_value(serie_node, val, x, y) |
|
|
|
|
|
|
|
|
|
return stack_vals |
|
|
|
|
x_center, y_center = self._bar(bar, x, y, index) |
|
|
|
|
self._tooltip_data( |
|
|
|
|
bar, val, x_center, y_center, classes="centered") |
|
|
|
|
self._static_value(serie_node, val, x_center, y_center) |
|
|
|
|
|
|
|
|
|
def _compute(self): |
|
|
|
|
self._box.ymin = min(self._min, self.zero) |
|
|
|
@ -120,18 +84,18 @@ class Bar(Graph):
|
|
|
|
|
x_pos = [ |
|
|
|
|
x / self._len for x in range(self._len + 1) |
|
|
|
|
] if self._len > 1 else [0, 1] # Center if only one value |
|
|
|
|
|
|
|
|
|
self._points(x_pos) |
|
|
|
|
|
|
|
|
|
y_pos = compute_scale( |
|
|
|
|
self._box.ymin, self._box.ymax, self.logarithmic, self.order_min |
|
|
|
|
) if not self.y_labels else map(float, self.y_labels) |
|
|
|
|
|
|
|
|
|
self._x_ranges = zip(x_pos, x_pos[1:]) |
|
|
|
|
# self._x_ranges = zip(x_pos, x_pos[1:]) |
|
|
|
|
self._x_labels = self.x_labels and zip(self.x_labels, [ |
|
|
|
|
sum(x_range) / 2 for x_range in self._x_ranges]) |
|
|
|
|
self._y_labels = zip(map(self._format, y_pos), y_pos) |
|
|
|
|
|
|
|
|
|
def _plot(self): |
|
|
|
|
for index, serie in enumerate(self.series): |
|
|
|
|
serie_node = self._serie(index) |
|
|
|
|
self.bar(serie_node, serie, [ |
|
|
|
|
tuple((self._x_ranges[i][j], v) for j in range(2)) |
|
|
|
|
for i, v in enumerate(serie.values)], index) |
|
|
|
|
self.bar(self._serie(index), serie, index) |
|
|
|
|