~openerp/openobject-server/web-dashboard

« back to all changes in this revision

Viewing changes to bin/pychart/interval_bar_plot.py

  • Committer: pinky
  • Date: 2006-12-07 13:41:40 UTC
  • Revision ID: pinky-3f10ee12cea3c4c75cef44ab04ad33ef47432907
New trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com)
 
3
 
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
 
7
# later version.
 
8
#
 
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
 
12
# for more details.
 
13
#
 
14
import line_style
 
15
import fill_style
 
16
import pychart_util
 
17
import chart_object
 
18
import legend
 
19
import bar_plot_doc
 
20
import theme
 
21
from types import *
 
22
from pychart_types import *
 
23
 
 
24
fill_styles = None
 
25
 
 
26
_keys = {
 
27
    "direction" : (StringType, "vertical",
 
28
                   """The direction the growth of the bars. The value is either 'horizontal'
 
29
                   or 'vertical'."""),
 
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
 
33
    "hcol"."""
 
34
    ),
 
35
    "data_label_offset": (CoordType, (0, 5),
 
36
                          "The location of data labels relative to the sample point. See also attribute data_label_format."),
 
37
    
 
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),
 
44
    
 
45
    "label": (StringType, "???", pychart_util.label_desc), 
 
46
    "bcol" : (IntType, 0,
 
47
              """Specifies the column from which base values (i.e., X values when attribute "direction" is "vertical", Y values otherwise) are extracted.
 
48
The
 
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.
 
52
              """),
 
53
    "hcol": (IntType, 1,
 
54
             """The column from which the base and height of
 
55
             bars are extracted. See the below example:
 
56
              
 
57
@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)
 
60
@end example
 
61
 
 
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,
 
65
              three bars are drawn,
 
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
 
71
              "fill_styles".
 
72
             """),
 
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:
 
89
 
 
90
@example
 
91
    a = area.T(...)
 
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))
 
94
    a.add_plot(p1, p2)
 
95
    a.draw()
 
96
@end example
 
97
 
 
98
    In this example, one group of bars will be drawn side-by-side at
 
99
    position x=1.
 
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.
 
102
    """),
 
103
    "width": (UnitType, 5, """Width of each box. The unit is in points.
 
104
@cindex width, bar chart
 
105
@cindex size, bar chart
 
106
"""),
 
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."),
 
111
    }
 
112
 
 
113
class T(chart_object.T):
 
114
    __doc__ = bar_plot_doc.doc
 
115
    keys = _keys
 
116
    def check_integrity(self):
 
117
        self.type_check()
 
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"
 
123
 
 
124
    def __get_data_range(self, col):
 
125
        gmin = 99999999
 
126
        gmax = -99999999
 
127
        for item in self.data:
 
128
            seq = item[col]
 
129
            if seq[0] < gmin: gmin = seq[0]
 
130
            max = 0
 
131
            for v in seq:
 
132
                max += v
 
133
            if max > gmax: gmax = max
 
134
        return (gmin, gmax)
 
135
    
 
136
    def get_data_range(self, which):
 
137
        if self.direction == 'vertical':
 
138
            if which == 'X':
 
139
                return pychart_util.get_data_range(self.data, self.bcol)
 
140
            else:
 
141
                return self.__get_data_range(self.hcol)
 
142
        else:
 
143
            assert self.direction == 'horizontal'
 
144
            if which == 'Y':
 
145
                return pychart_util.get_data_range(self.data, self.bcol)
 
146
            else:
 
147
                return self.__get_data_range(self.hcol)
 
148
 
 
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)
 
153
    
 
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)
 
158
            
 
159
            if None in (xval, yvals): continue
 
160
 
 
161
            ybot = 0
 
162
            
 
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
 
166
 
 
167
            cury = yvals[0]
 
168
            n = 0
 
169
            
 
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))
 
175
                cury += yval
 
176
                n += 1
 
177
                
 
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))
 
182
            
 
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)
 
187
 
 
188
            if None in (xvals, yval): continue
 
189
 
 
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
 
193
 
 
194
            curx = xvals[0]
 
195
            n = 0
 
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)
 
201
                curx = xval
 
202
                n += 1
 
203
                
 
204
    def get_legend_entry(self):
 
205
        if self.label:
 
206
            return legend.Entry(line_style=self.line_styles[0],
 
207
                                fill_style=self.fill_styles[0],
 
208
                                label=self.label)
 
209
        return None
 
210
        
 
211
    def draw(self, ar, can):
 
212
        self.type_check()
 
213
        can.clip(ar.loc[0], ar.loc[1],
 
214
                 ar.loc[0] + ar.size[0], ar.loc[1] + ar.size[1])
 
215
            
 
216
        if self.direction == "vertical":
 
217
            self.draw_vertical(ar, can)
 
218
        else:
 
219
            self.draw_horizontal(ar, can)
 
220
 
 
221
        can.endclip()
 
222
 
 
223
def init():
 
224
    global fill_styles
 
225
    fill_styles = fill_style.standards.iterate()
 
226
    
 
227
theme.add_reinitialization_hook(init)
 
228