2
# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com)
4
# Jockey is free software; you can redistribute it and/or modify it
5
# under the terms of the GNU General Public License as published by the
6
# Free Software Foundation; either version 2, or (at your option) any
9
# Jockey is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22
from pychart_types import *
27
"direction" : (StringType, "vertical",
28
"""The direction the growth of the bars. The value is either 'horizontal'
30
"data" : (AnyType, None, """Specifes data points. Unlike other types
31
of charts, the "hcol"th column of the data must be a sequence of
32
numbers, not just a single number. See also the description of
35
"data_label_offset": (CoordType, (0, 5),
36
"The location of data labels relative to the sample point. See also attribute data_label_format."),
38
"data_label_format": (FormatType, None, """The
39
format string for the label displayed besides each
40
bar. It can be a `printf' style format
41
string, or a two-parameter function that
42
takes (x,y) values and returns a string. """
43
+ pychart_util.string_desc),
45
"label": (StringType, "???", pychart_util.label_desc),
47
"""Specifies the column from which base values (i.e., X values when attribute "direction" is "vertical", Y values otherwise) are extracted.
49
combination of "data", "bcol", and "hcol" attributes defines
50
the set of boxes drawn by this chart.
51
See also the descriptions of the 'bcol' and 'data' attributes.
54
"""The column from which the base and height of
55
bars are extracted. See the below example:
58
d = [[5,[10,15,22]], [7,[22,23,5,10]], [8,[25,3]]]
59
p = interval_bar_plot.T(data = d, bcol = 0, hcol = 1)
62
Here, three sequence of bars will be drawn.
63
The X locations of the bars
64
will be 5, 7, and 8. For example, at location X=7,
66
one corresponding to Y values of 22 to 45 (=22+23),
67
and the second one for values 45 to 50, and the third one
68
for values 50 to 60. The line and fill styles of the bars
69
are picked in a round-robin fashion
70
from attributes "line_styles" and
73
"line_styles": (ListType, [line_style.default, None],
74
"""The list of line styles for bars.
75
The style of each bar is chosen in a round-robin fashion, if the
76
number of elements in "line_styles" is smaller than
77
actual number of boxes."""),
78
"fill_styles": (ListType, [lambda: fill_styles.next(), None],
79
"""List of fill styles for bars.
80
The style of each bar is chosen in a round-robin fashion, if the
81
number of elements in "line_styles" is smaller than
82
actual number of boxes.
83
If this attribute is omitted,
84
a style is picked from standard styles round-robin. <<fill_style>>."""),
85
"cluster": (TupleType, (0, 1), """This attribute is used to
86
cluster multiple bar plots side by side in a single chart.
87
The value should be a tuple of two integers. The second value should be equal to the total number of bar plots in the chart. The first value should be the relative position of this chart; 0 places this chart the leftmost, and N-1
88
(where N is the 2nd value of this attribute) places this chart the rightmost. Consider the below example:
92
p1 = interval_bar_plot.T(data = [[1, [20,10]][2,[30,5]]], cluster=(0,2))
93
p2 = interval_bar_plot.T(data = [[1,[25,11,2]],[2,[10,5,3]]], cluster=(1,2))
98
In this example, one group of bars will be drawn side-by-side at
100
Other two bars will be drawn side by side at position x=2.
101
See also the description of attribute "cluster" for bar_plot.T.
103
"width": (UnitType, 5, """Width of each box. The unit is in points.
104
@cindex width, bar chart
105
@cindex size, bar chart
107
"cluster_sep": (UnitType, 0, """The separation between
108
clustered boxes. The unit is points."""),
109
"stack_on": (AnyType, None,
110
"The value must be either None or bar_plot.T. If not None, bars of this plot are stacked on top of another bar plot."),
113
class T(chart_object.T):
114
__doc__ = bar_plot_doc.doc
116
def check_integrity(self):
118
def get_value(self, bval):
119
for pair in self.data:
120
if pair[self.bcol] == bval:
121
return pair[self.hcol]
122
raise ValueError, str(bval) + ": can't find the xval"
124
def __get_data_range(self, col):
127
for item in self.data:
129
if seq[0] < gmin: gmin = seq[0]
133
if max > gmax: gmax = max
136
def get_data_range(self, which):
137
if self.direction == 'vertical':
139
return pychart_util.get_data_range(self.data, self.bcol)
141
return self.__get_data_range(self.hcol)
143
assert self.direction == 'horizontal'
145
return pychart_util.get_data_range(self.data, self.bcol)
147
return self.__get_data_range(self.hcol)
149
def get_style(self, nth):
150
line_style = self.line_styles[nth % len(self.line_styles)]
151
fill_style = self.fill_styles[nth % len(self.fill_styles)]
152
return (line_style, fill_style)
154
def draw_vertical(self, ar, can):
155
for pair in self.data:
156
xval = pair[self.bcol]
157
yvals = pychart_util.get_sample_val(pair, self.hcol)
159
if None in (xval, yvals): continue
163
totalWidth = (self.width+self.cluster_sep) * self.cluster[1] - self.cluster_sep
164
firstX = ar.x_pos(xval) - totalWidth/2.0
165
thisX = firstX + (self.width+self.cluster_sep) * self.cluster[0] - self.cluster_sep
170
for yval in yvals[1:]:
171
(line_style, fill_style) = self.get_style(n)
172
can.rectangle(line_style, fill_style,
173
thisX, ar.y_pos(cury), thisX+self.width,
174
ar.y_pos(cury + yval))
178
if self.data_label_format:
179
can.show(thisX + self.width/2.0 + self.data_label_offset[0],
180
ar.y_pos(cury) + self.data_label_offset[1],
181
"/hC" + pychart_util.apply_format(self.data_label_format, (pair[self.bcol], pair[self.hcol]), 1))
183
def draw_horizontal(self, ar, can):
184
for pair in self.data:
185
yval = pair[self.bcol]
186
xvals = pychart_util.get_sample_val(pair, self.hcol)
188
if None in (xvals, yval): continue
190
totalWidth = (self.width+self.cluster_sep) * self.cluster[1] - self.cluster_sep
191
firstY = ar.y_pos(yval) - totalWidth/2.0
192
thisY = firstY + (self.width+self.cluster_sep) * self.cluster[0] - self.cluster_sep
196
for xval in xvals[1:]:
197
line_style, fill_style = self.get_style(n)
198
can.rectangle(line_style, fill_style,
199
ar.x_pos(curx), thisY,
200
ar.x_pos(xval), thisY+self.width)
204
def get_legend_entry(self):
206
return legend.Entry(line_style=self.line_styles[0],
207
fill_style=self.fill_styles[0],
211
def draw(self, ar, can):
213
can.clip(ar.loc[0], ar.loc[1],
214
ar.loc[0] + ar.size[0], ar.loc[1] + ar.size[1])
216
if self.direction == "vertical":
217
self.draw_vertical(ar, can)
219
self.draw_horizontal(ar, can)
225
fill_styles = fill_style.standards.iterate()
227
theme.add_reinitialization_hook(init)