| 1 | |
|---|
| 2 | |
|---|
| 3 | |
|---|
| 4 | |
|---|
| 5 | |
|---|
| 6 | |
|---|
| 7 | |
|---|
| 8 | |
|---|
| 9 | |
|---|
| 10 | |
|---|
| 11 | |
|---|
| 12 | |
|---|
| 13 | |
|---|
| 14 | |
|---|
| 15 | |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | from pycha.chart import Chart |
|---|
| 19 | from pycha.color import hex2rgb |
|---|
| 20 | |
|---|
| 21 | |
|---|
| 22 | class LineChart(Chart): |
|---|
| 23 | |
|---|
| 24 | def __init__(self, surface=None, options={}): |
|---|
| 25 | super(LineChart, self).__init__(surface, options) |
|---|
| 26 | self.points = [] |
|---|
| 27 | |
|---|
| 28 | def _updateChart(self): |
|---|
| 29 | """Evaluates measures for line charts""" |
|---|
| 30 | self.points = [] |
|---|
| 31 | |
|---|
| 32 | for i, (name, store) in enumerate(self.datasets): |
|---|
| 33 | for item in store: |
|---|
| 34 | xval, yval = item |
|---|
| 35 | x = (xval - self.minxval) * self.xscale |
|---|
| 36 | y = 1.0 - (yval - self.minyval) * self.yscale |
|---|
| 37 | point = Point(x, y, xval, yval, name) |
|---|
| 38 | |
|---|
| 39 | if 0.0 <= point.x <= 1.0 and 0.0 <= point.y <= 1.0: |
|---|
| 40 | self.points.append(point) |
|---|
| 41 | |
|---|
| 42 | def _renderChart(self, cx): |
|---|
| 43 | """Renders a line chart""" |
|---|
| 44 | |
|---|
| 45 | def preparePath(storeName): |
|---|
| 46 | cx.new_path() |
|---|
| 47 | firstPoint = True |
|---|
| 48 | lastX = None |
|---|
| 49 | if self.options.shouldFill: |
|---|
| 50 | |
|---|
| 51 | |
|---|
| 52 | offset = (1.0 - self.area.origin) * self.area.h |
|---|
| 53 | cx.move_to(self.area.x, self.area.y + offset) |
|---|
| 54 | |
|---|
| 55 | for point in self.points: |
|---|
| 56 | if point.name == storeName: |
|---|
| 57 | if not self.options.shouldFill and firstPoint: |
|---|
| 58 | |
|---|
| 59 | cx.move_to(point.x * self.area.w + self.area.x, |
|---|
| 60 | point.y * self.area.h + self.area.y) |
|---|
| 61 | firstPoint = False |
|---|
| 62 | continue |
|---|
| 63 | cx.line_to(point.x * self.area.w + self.area.x, |
|---|
| 64 | point.y * self.area.h + self.area.y) |
|---|
| 65 | |
|---|
| 66 | |
|---|
| 67 | lastX = point.x |
|---|
| 68 | |
|---|
| 69 | if self.options.shouldFill: |
|---|
| 70 | |
|---|
| 71 | y = (1.0 - self.area.origin) * self.area.h + self.area.y |
|---|
| 72 | cx.line_to(lastX * self.area.w + self.area.x, y) |
|---|
| 73 | cx.line_to(self.area.x, y) |
|---|
| 74 | cx.close_path() |
|---|
| 75 | else: |
|---|
| 76 | cx.set_source_rgb(*self.colorScheme[storeName]) |
|---|
| 77 | cx.stroke() |
|---|
| 78 | |
|---|
| 79 | |
|---|
| 80 | cx.save() |
|---|
| 81 | cx.set_line_width(self.options.stroke.width) |
|---|
| 82 | if self.options.shouldFill: |
|---|
| 83 | |
|---|
| 84 | def drawLine(storeName): |
|---|
| 85 | if self.options.stroke.shadow: |
|---|
| 86 | |
|---|
| 87 | cx.save() |
|---|
| 88 | cx.set_source_rgba(0, 0, 0, 0.15) |
|---|
| 89 | cx.translate(2, -2) |
|---|
| 90 | preparePath(storeName) |
|---|
| 91 | cx.fill() |
|---|
| 92 | cx.restore() |
|---|
| 93 | |
|---|
| 94 | |
|---|
| 95 | cx.set_source_rgb(*self.colorScheme[storeName]) |
|---|
| 96 | preparePath(storeName) |
|---|
| 97 | cx.fill() |
|---|
| 98 | |
|---|
| 99 | if not self.options.stroke.hide: |
|---|
| 100 | |
|---|
| 101 | cx.set_source_rgb(*hex2rgb(self.options.stroke.color)) |
|---|
| 102 | preparePath(storeName) |
|---|
| 103 | cx.stroke() |
|---|
| 104 | |
|---|
| 105 | |
|---|
| 106 | for key in self._getDatasetsKeys(): |
|---|
| 107 | drawLine(key) |
|---|
| 108 | else: |
|---|
| 109 | for key in self._getDatasetsKeys(): |
|---|
| 110 | preparePath(key) |
|---|
| 111 | |
|---|
| 112 | cx.restore() |
|---|
| 113 | |
|---|
| 114 | |
|---|
| 115 | class Point(object): |
|---|
| 116 | |
|---|
| 117 | def __init__(self, x, y, xval, yval, name): |
|---|
| 118 | self.x, self.y = x, y |
|---|
| 119 | self.xval, self.yval = xval, yval |
|---|
| 120 | self.name = name |
|---|
| 121 | |
|---|
| 122 | def __str__(self): |
|---|
| 123 | return "<pycha.line.Point@(%.2f, %.2f)>" % (self.x, self.y) |
|---|