root/tags/0.4.1/src/bar.py

Revision 112, 7.0 kB (checked in by lgs, 4 years ago)

Remove whitespace

Line 
1# Copyright (c) 2007-2008 by Lorenzo Gil Sanchez <lorenzo.gil.sanchez@gmail.com>
2#
3# This file is part of PyCha.
4#
5# PyCha is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# PyCha is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU Lesser General Public License for more details.
14#
15# You should have received a copy of the GNU Lesser General Public License
16# along with PyCha.  If not, see <http://www.gnu.org/licenses/>.
17
18from pycha.chart import Chart, uniqueIndices
19from pycha.color import hex2rgb
20
21class BarChart(Chart):
22
23    def __init__(self, surface=None, options={}):
24        super(BarChart, self).__init__(surface, options)
25        self.bars = []
26        self.minxdelta = 0.0
27        self.barWidthForSet = 0.0
28        self.barMargin = 0.0
29
30    def _updateXY(self):
31        super(BarChart, self)._updateXY()
32        # each dataset is centered around a line segment. that's why we
33        # need n + 1 divisions on the x axis
34        self.xscale = 1 / (self.xrange + 1.0)
35
36    def _updateChart(self):
37        """Evaluates measures for vertical bars"""
38        stores = self._getDatasetsValues()
39        uniqx = uniqueIndices(stores)
40
41        barWidth = 0
42        if len(uniqx) == 1:
43            xdelta = 1.0
44            barWidth = 1.0 * self.options.barWidthFillFraction
45            self.barWidthForSet = barWidth / len(stores)
46            self.barMargin = (1.0 - self.options.barWidthFillFraction) / 2
47        else:
48            xdelta = min([abs(uniqx[j] - uniqx[j-1])
49                          for j in range(1, len(uniqx))])
50            barWidth = xdelta * self.xscale * self.options.barWidthFillFraction
51            self.barWidthForSet = barWidth / len(stores)
52            self.barMargin = (xdelta * self.xscale
53                              * (1.0 - self.options.barWidthFillFraction) / 2)
54
55        self.minxdelta = xdelta
56        self.bars = []
57
58    def _renderChart(self, cx):
59        """Renders a horizontal/vertical bar chart"""
60
61        def drawBar(bar):
62            stroke_width = self.options.stroke.width
63            ux, uy = cx.device_to_user_distance(stroke_width, stroke_width)
64            if ux < uy:
65                ux = uy
66            cx.set_line_width(ux)
67
68            # gather bar proportions
69            x = self.area.x + self.area.w * bar.x
70            y = self.area.y + self.area.h * bar.y
71            w = self.area.w * bar.w
72            h = self.area.h * bar.h
73
74            if w < 1 or h < 1:
75                return # don't draw when the bar is too small
76
77            if self.options.stroke.shadow:
78                cx.set_source_rgba(0, 0, 0, 0.15)
79                rectangle = self._getShadowRectangle(x, y, w, h)
80                cx.rectangle(*rectangle)
81                cx.fill()
82
83            if self.options.shouldFill or (not self.options.stroke.hide):
84                cx.rectangle(x, y, w, h)
85
86                if self.options.shouldFill:
87                    cx.set_source_rgb(*self.options.colorScheme[bar.name])
88                    cx.fill_preserve()
89
90                if not self.options.stroke.hide:
91                    cx.set_source_rgb(*hex2rgb(self.options.stroke.color))
92                    cx.stroke()
93
94        cx.save()
95        for bar in self.bars:
96            drawBar(bar)
97        cx.restore()
98
99class VerticalBarChart(BarChart):
100
101    def _updateChart(self):
102        """Evaluates measures for vertical bars"""
103        super(VerticalBarChart, self)._updateChart()
104        for i, (name, store) in enumerate(self.datasets):
105            for item in store:
106                xval, yval = item
107                x = (((xval - self.minxval) * self.xscale)
108                    + self.barMargin + (i * self.barWidthForSet))
109                w = self.barWidthForSet
110                h = abs(yval) * self.yscale
111                if yval > 0:
112                    y = (1.0 - h) - self.area.origin
113                else:
114                    y = 1 - self.area.origin
115                rect = Rect(x, y, w, h, xval, yval, name)
116
117                if (0.0 <= rect.x <= 1.0) and (0.0 <= rect.y <= 1.0):
118                    self.bars.append(rect)
119
120    def _updateTicks(self):
121        """Evaluates bar ticks"""
122        super(BarChart, self)._updateTicks()
123        offset = (self.minxdelta * self.xscale) / 2
124        self.xticks = [(tick[0] + offset, tick[1]) for tick in self.xticks]
125
126    def _getShadowRectangle(self, x, y, w, h):
127        return (x-2, y-2, w+4, h+2)
128
129
130class HorizontalBarChart(BarChart):
131
132    def _updateChart(self):
133        """Evaluates measures for horizontal bars"""
134        super(HorizontalBarChart, self)._updateChart()
135
136        for i, (name, store) in enumerate(self.datasets):
137            for item in store:
138                xval, yval = item
139                y = (((xval - self.minxval) * self.xscale)
140                     + self.barMargin  + (i * self.barWidthForSet))
141                h = self.barWidthForSet
142                w = abs(yval) * self.yscale
143                if yval > 0:
144                    x = self.area.origin
145                else:
146                    x = self.area.origin - w
147                rect = Rect(x, y, w, h, xval, yval, name)
148
149                if (0.0 <= rect.x <= 1.0) and (0.0 <= rect.y <= 1.0):
150                    self.bars.append(rect)
151
152    def _updateTicks(self):
153        """Evaluates bar ticks"""
154        super(BarChart, self)._updateTicks()
155        offset = (self.minxdelta * self.xscale) / 2
156        tmp = self.xticks
157        self.xticks = [(1.0 - tick[0], tick[1]) for tick in self.yticks ]
158        self.yticks = [(tick[0] + offset, tick[1]) for tick in tmp]
159
160    def _renderLines(self, cx):
161        """Aux function for _renderBackground"""
162        ticks = self.xticks
163        for tick in ticks:
164            self._renderLine(cx, tick, True)
165
166    def _getShadowRectangle(self, x, y, w, h):
167        return (x, y-2, w+2, h+4)
168
169    def _renderXAxis(self, cx):
170        """Draws the horizontal line representing the X axis"""
171        cx.new_path()
172        cx.move_to(self.area.x, self.area.y + self.area.h)
173        cx.line_to(self.area.x + self.area.w, self.area.y + self.area.h)
174        cx.close_path()
175        cx.stroke()
176
177    def _renderYAxis(self, cx):
178        # draws the vertical line representing the Y axis
179        cx.new_path()
180        cx.move_to(self.area.x + self.area.origin * self.area.w,
181                   self.area.y)
182        cx.line_to(self.area.x + self.area.origin * self.area.w,
183                   self.area.y + self.area.h)
184        cx.close_path()
185        cx.stroke()
186
187class Rect(object):
188    def __init__(self, x, y, w, h, xval, yval, name):
189        self.x, self.y, self.w, self.h = x, y, w, h
190        self.xval, self.yval = xval, yval
191        self.name = name
192
193    def __str__(self):
194        return ("<pycha.bar.Rect@(%.2f, %.2f) %.2fx%.2f (%.2f, %.2f) %s>"
195                % (self.x, self.y, self.w, self.h, self.xval, self.yval,
196                   self.name))
Note: See TracBrowser for help on using the browser.