Added Python 3.0 or better requirement to the README file
[babeltrace.git] / bindings / python / examples / output_format_modules / cairoplot.py
CommitLineData
24a3136a
DS
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# CairoPlot.py
5#
6# Copyright (c) 2008 Rodrigo Moreira Araújo
7#
8# Author: Rodrigo Moreiro Araujo <alf.rodrigo@gmail.com>
9#
10# This program is free software; you can redistribute it and/or
11# modify it under the terms of the GNU Lesser General Public License
12# as published by the Free Software Foundation; either version 2 of
13# the License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU Lesser General Public
21# License along with this program; if not, write to the Free Software
22# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23# USA
24
25#Contributor: João S. O. Bueno
26
27#TODO: review BarPlot Code
28#TODO: x_label colision problem on Horizontal Bar Plot
29#TODO: y_label's eat too much space on HBP
30
31
32__version__ = 1.2
33
34import cairo
35import math
36import random
37from series import Series, Group, Data
38
39HORZ = 0
40VERT = 1
41NORM = 2
42
43COLORS = {"red" : (1.0,0.0,0.0,1.0), "lime" : (0.0,1.0,0.0,1.0), "blue" : (0.0,0.0,1.0,1.0),
44 "maroon" : (0.5,0.0,0.0,1.0), "green" : (0.0,0.5,0.0,1.0), "navy" : (0.0,0.0,0.5,1.0),
45 "yellow" : (1.0,1.0,0.0,1.0), "magenta" : (1.0,0.0,1.0,1.0), "cyan" : (0.0,1.0,1.0,1.0),
46 "orange" : (1.0,0.5,0.0,1.0), "white" : (1.0,1.0,1.0,1.0), "black" : (0.0,0.0,0.0,1.0),
47 "gray" : (0.5,0.5,0.5,1.0), "light_gray" : (0.9,0.9,0.9,1.0),
48 "transparent" : (0.0,0.0,0.0,0.0)}
49
50THEMES = {"black_red" : [(0.0,0.0,0.0,1.0), (1.0,0.0,0.0,1.0)],
51 "red_green_blue" : [(1.0,0.0,0.0,1.0), (0.0,1.0,0.0,1.0), (0.0,0.0,1.0,1.0)],
52 "red_orange_yellow" : [(1.0,0.2,0.0,1.0), (1.0,0.7,0.0,1.0), (1.0,1.0,0.0,1.0)],
53 "yellow_orange_red" : [(1.0,1.0,0.0,1.0), (1.0,0.7,0.0,1.0), (1.0,0.2,0.0,1.0)],
54 "rainbow" : [(1.0,0.0,0.0,1.0), (1.0,0.5,0.0,1.0), (1.0,1.0,0.0,1.0), (0.0,1.0,0.0,1.0), (0.0,0.0,1.0,1.0), (0.3, 0.0, 0.5,1.0), (0.5, 0.0, 1.0, 1.0)]}
55
56def colors_from_theme( theme, series_length, mode = 'solid' ):
57 colors = []
58 if theme not in THEMES.keys() :
59 raise Exception, "Theme not defined"
60 color_steps = THEMES[theme]
61 n_colors = len(color_steps)
62 if series_length <= n_colors:
63 colors = [color + tuple([mode]) for color in color_steps[0:n_colors]]
64 else:
65 iterations = [(series_length - n_colors)/(n_colors - 1) for i in color_steps[:-1]]
66 over_iterations = (series_length - n_colors) % (n_colors - 1)
67 for i in range(n_colors - 1):
68 if over_iterations <= 0:
69 break
70 iterations[i] += 1
71 over_iterations -= 1
72 for index,color in enumerate(color_steps[:-1]):
73 colors.append(color + tuple([mode]))
74 if iterations[index] == 0:
75 continue
76 next_color = color_steps[index+1]
77 color_step = ((next_color[0] - color[0])/(iterations[index] + 1),
78 (next_color[1] - color[1])/(iterations[index] + 1),
79 (next_color[2] - color[2])/(iterations[index] + 1),
80 (next_color[3] - color[3])/(iterations[index] + 1))
81 for i in range( iterations[index] ):
82 colors.append((color[0] + color_step[0]*(i+1),
83 color[1] + color_step[1]*(i+1),
84 color[2] + color_step[2]*(i+1),
85 color[3] + color_step[3]*(i+1),
86 mode))
87 colors.append(color_steps[-1] + tuple([mode]))
88 return colors
89
90
91def other_direction(direction):
92 "explicit is better than implicit"
93 if direction == HORZ:
94 return VERT
95 else:
96 return HORZ
97
98#Class definition
99
100class Plot(object):
101 def __init__(self,
102 surface=None,
103 data=None,
104 width=640,
105 height=480,
106 background=None,
107 border = 0,
108 x_labels = None,
109 y_labels = None,
110 series_colors = None):
111 random.seed(2)
112 self.create_surface(surface, width, height)
113 self.dimensions = {}
114 self.dimensions[HORZ] = width
115 self.dimensions[VERT] = height
116 self.context = cairo.Context(self.surface)
117 self.labels={}
118 self.labels[HORZ] = x_labels
119 self.labels[VERT] = y_labels
120 self.load_series(data, x_labels, y_labels, series_colors)
121 self.font_size = 10
122 self.set_background (background)
123 self.border = border
124 self.borders = {}
125 self.line_color = (0.5, 0.5, 0.5)
126 self.line_width = 0.5
127 self.label_color = (0.0, 0.0, 0.0)
128 self.grid_color = (0.8, 0.8, 0.8)
129
130 def create_surface(self, surface, width=None, height=None):
131 self.filename = None
132 if isinstance(surface, cairo.Surface):
133 self.surface = surface
134 return
135 if not type(surface) in (str, unicode):
136 raise TypeError("Surface should be either a Cairo surface or a filename, not %s" % surface)
137 sufix = surface.rsplit(".")[-1].lower()
138 self.filename = surface
139 if sufix == "png":
140 self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
141 elif sufix == "ps":
142 self.surface = cairo.PSSurface(surface, width, height)
143 elif sufix == "pdf":
144 self.surface = cairo.PSSurface(surface, width, height)
145 else:
146 if sufix != "svg":
147 self.filename += ".svg"
148 self.surface = cairo.SVGSurface(self.filename, width, height)
149
150 def commit(self):
151 try:
152 self.context.show_page()
153 if self.filename and self.filename.endswith(".png"):
154 self.surface.write_to_png(self.filename)
155 else:
156 self.surface.finish()
157 except cairo.Error:
158 pass
159
160 def load_series (self, data, x_labels=None, y_labels=None, series_colors=None):
161 self.series_labels = []
162 self.series = None
163
164 #The pretty way
165 #if not isinstance(data, Series):
166 # # Not an instance of Series
167 # self.series = Series(data)
168 #else:
169 # self.series = data
170 #
171 #self.series_labels = self.series.get_names()
172
173 #TODO: Remove on next version
174 # The ugly way, keeping retrocompatibility...
175 if callable(data) or type(data) is list and callable(data[0]): # Lambda or List of lambdas
176 self.series = data
177 self.series_labels = None
178 elif isinstance(data, Series): # Instance of Series
179 self.series = data
180 self.series_labels = data.get_names()
181 else: # Anything else
182 self.series = Series(data)
183 self.series_labels = self.series.get_names()
184
185 #TODO: allow user passed series_widths
186 self.series_widths = [1.0 for group in self.series]
187
188 #TODO: Remove on next version
189 self.process_colors( series_colors )
190
191 def process_colors( self, series_colors, length = None, mode = 'solid' ):
192 #series_colors might be None, a theme, a string of colors names or a list of color tuples
193 if length is None :
194 length = len( self.series.to_list() )
195
196 #no colors passed
197 if not series_colors:
198 #Randomize colors
199 self.series_colors = [ [random.random() for i in range(3)] + [1.0, mode] for series in range( length ) ]
200 else:
201 #Just theme pattern
202 if not hasattr( series_colors, "__iter__" ):
203 theme = series_colors
204 self.series_colors = colors_from_theme( theme.lower(), length )
205
206 #Theme pattern and mode
207 elif not hasattr(series_colors, '__delitem__') and not hasattr( series_colors[0], "__iter__" ):
208 theme = series_colors[0]
209 mode = series_colors[1]
210 self.series_colors = colors_from_theme( theme.lower(), length, mode )
211
212 #List
213 else:
214 self.series_colors = series_colors
215 for index, color in enumerate( self.series_colors ):
216 #element is a color name
217 if not hasattr(color, "__iter__"):
218 self.series_colors[index] = COLORS[color.lower()] + tuple([mode])
219 #element is rgb tuple instead of rgba
220 elif len( color ) == 3 :
221 self.series_colors[index] += (1.0,mode)
222 #element has 4 elements, might be rgba tuple or rgb tuple with mode
223 elif len( color ) == 4 :
224 #last element is mode
225 if not hasattr(color[3], "__iter__"):
226 self.series_colors[index] += tuple([color[3]])
227 self.series_colors[index][3] = 1.0
228 #last element is alpha
229 else:
230 self.series_colors[index] += tuple([mode])
231
232 def get_width(self):
233 return self.surface.get_width()
234
235 def get_height(self):
236 return self.surface.get_height()
237
238 def set_background(self, background):
239 if background is None:
240 self.background = (0.0,0.0,0.0,0.0)
241 elif type(background) in (cairo.LinearGradient, tuple):
242 self.background = background
243 elif not hasattr(background,"__iter__"):
244 colors = background.split(" ")
245 if len(colors) == 1 and colors[0] in COLORS:
246 self.background = COLORS[background]
247 elif len(colors) > 1:
248 self.background = cairo.LinearGradient(self.dimensions[HORZ] / 2, 0, self.dimensions[HORZ] / 2, self.dimensions[VERT])
249 for index,color in enumerate(colors):
250 self.background.add_color_stop_rgba(float(index)/(len(colors)-1),*COLORS[color])
251 else:
252 raise TypeError ("Background should be either cairo.LinearGradient or a 3/4-tuple, not %s" % type(background))
253
254 def render_background(self):
255 if isinstance(self.background, cairo.LinearGradient):
256 self.context.set_source(self.background)
257 else:
258 self.context.set_source_rgba(*self.background)
259 self.context.rectangle(0,0, self.dimensions[HORZ], self.dimensions[VERT])
260 self.context.fill()
261
262 def render_bounding_box(self):
263 self.context.set_source_rgba(*self.line_color)
264 self.context.set_line_width(self.line_width)
265 self.context.rectangle(self.border, self.border,
266 self.dimensions[HORZ] - 2 * self.border,
267 self.dimensions[VERT] - 2 * self.border)
268 self.context.stroke()
269
270 def render(self):
271 pass
272
273class ScatterPlot( Plot ):
274 def __init__(self,
275 surface=None,
276 data=None,
277 errorx=None,
278 errory=None,
279 width=640,
280 height=480,
281 background=None,
282 border=0,
283 axis = False,
284 dash = False,
285 discrete = False,
286 dots = 0,
287 grid = False,
288 series_legend = False,
289 x_labels = None,
290 y_labels = None,
291 x_bounds = None,
292 y_bounds = None,
293 z_bounds = None,
294 x_title = None,
295 y_title = None,
296 series_colors = None,
297 circle_colors = None ):
298
299 self.bounds = {}
300 self.bounds[HORZ] = x_bounds
301 self.bounds[VERT] = y_bounds
302 self.bounds[NORM] = z_bounds
303 self.titles = {}
304 self.titles[HORZ] = x_title
305 self.titles[VERT] = y_title
306 self.max_value = {}
307 self.axis = axis
308 self.discrete = discrete
309 self.dots = dots
310 self.grid = grid
311 self.series_legend = series_legend
312 self.variable_radius = False
313 self.x_label_angle = math.pi / 2.5
314 self.circle_colors = circle_colors
315
316 Plot.__init__(self, surface, data, width, height, background, border, x_labels, y_labels, series_colors)
317
318 self.dash = None
319 if dash:
320 if hasattr(dash, "keys"):
321 self.dash = [dash[key] for key in self.series_labels]
322 elif max([hasattr(item,'__delitem__') for item in data]) :
323 self.dash = dash
324 else:
325 self.dash = [dash]
326
327 self.load_errors(errorx, errory)
328
329 def convert_list_to_tuple(self, data):
330 #Data must be converted from lists of coordinates to a single
331 # list of tuples
332 out_data = zip(*data)
333 if len(data) == 3:
334 self.variable_radius = True
335 return out_data
336
337 def load_series(self, data, x_labels = None, y_labels = None, series_colors=None):
338 #TODO: In cairoplot 2.0 keep only the Series instances
339
340 # Convert Data and Group to Series
341 if isinstance(data, Data) or isinstance(data, Group):
342 data = Series(data)
343
344 # Series
345 if isinstance(data, Series):
346 for group in data:
347 for item in group:
348 if len(item) is 3:
349 self.variable_radius = True
350
351 #Dictionary with lists
352 if hasattr(data, "keys") :
353 if hasattr( data.values()[0][0], "__delitem__" ) :
354 for key in data.keys() :
355 data[key] = self.convert_list_to_tuple(data[key])
356 elif len(data.values()[0][0]) == 3:
357 self.variable_radius = True
358 #List
359 elif hasattr(data[0], "__delitem__") :
360 #List of lists
361 if hasattr(data[0][0], "__delitem__") :
362 for index,value in enumerate(data) :
363 data[index] = self.convert_list_to_tuple(value)
364 #List
365 elif type(data[0][0]) != type((0,0)):
366 data = self.convert_list_to_tuple(data)
367 #Three dimensional data
368 elif len(data[0][0]) == 3:
369 self.variable_radius = True
370
371 #List with three dimensional tuples
372 elif len(data[0]) == 3:
373 self.variable_radius = True
374 Plot.load_series(self, data, x_labels, y_labels, series_colors)
375 self.calc_boundaries()
376 self.calc_labels()
377
378 def load_errors(self, errorx, errory):
379 self.errors = None
380 if errorx == None and errory == None:
381 return
382 self.errors = {}
383 self.errors[HORZ] = None
384 self.errors[VERT] = None
385 #asimetric errors
386 if errorx and hasattr(errorx[0], "__delitem__"):
387 self.errors[HORZ] = errorx
388 #simetric errors
389 elif errorx:
390 self.errors[HORZ] = [errorx]
391 #asimetric errors
392 if errory and hasattr(errory[0], "__delitem__"):
393 self.errors[VERT] = errory
394 #simetric errors
395 elif errory:
396 self.errors[VERT] = [errory]
397
398 def calc_labels(self):
399 if not self.labels[HORZ]:
400 amplitude = self.bounds[HORZ][1] - self.bounds[HORZ][0]
401 if amplitude % 10: #if horizontal labels need floating points
402 self.labels[HORZ] = ["%.2lf" % (float(self.bounds[HORZ][0] + (amplitude * i / 10.0))) for i in range(11) ]
403 else:
404 self.labels[HORZ] = ["%d" % (int(self.bounds[HORZ][0] + (amplitude * i / 10.0))) for i in range(11) ]
405 if not self.labels[VERT]:
406 amplitude = self.bounds[VERT][1] - self.bounds[VERT][0]
407 if amplitude % 10: #if vertical labels need floating points
408 self.labels[VERT] = ["%.2lf" % (float(self.bounds[VERT][0] + (amplitude * i / 10.0))) for i in range(11) ]
409 else:
410 self.labels[VERT] = ["%d" % (int(self.bounds[VERT][0] + (amplitude * i / 10.0))) for i in range(11) ]
411
412 def calc_extents(self, direction):
413 self.context.set_font_size(self.font_size * 0.8)
414 self.max_value[direction] = max(self.context.text_extents(item)[2] for item in self.labels[direction])
415 self.borders[other_direction(direction)] = self.max_value[direction] + self.border + 20
416
417 def calc_boundaries(self):
418 #HORZ = 0, VERT = 1, NORM = 2
419 min_data_value = [0,0,0]
420 max_data_value = [0,0,0]
421
422 for group in self.series:
423 if type(group[0].content) in (int, float, long):
424 group = [Data((index, item.content)) for index,item in enumerate(group)]
425
426 for point in group:
427 for index, item in enumerate(point.content):
428 if item > max_data_value[index]:
429 max_data_value[index] = item
430 elif item < min_data_value[index]:
431 min_data_value[index] = item
432
433 if not self.bounds[HORZ]:
434 self.bounds[HORZ] = (min_data_value[HORZ], max_data_value[HORZ])
435 if not self.bounds[VERT]:
436 self.bounds[VERT] = (min_data_value[VERT], max_data_value[VERT])
437 if not self.bounds[NORM]:
438 self.bounds[NORM] = (min_data_value[NORM], max_data_value[NORM])
439
440 def calc_all_extents(self):
441 self.calc_extents(HORZ)
442 self.calc_extents(VERT)
443
444 self.plot_height = self.dimensions[VERT] - 2 * self.borders[VERT]
445 self.plot_width = self.dimensions[HORZ] - 2* self.borders[HORZ]
446
447 self.plot_top = self.dimensions[VERT] - self.borders[VERT]
448
449 def calc_steps(self):
450 #Calculates all the x, y, z and color steps
451 series_amplitude = [self.bounds[index][1] - self.bounds[index][0] for index in range(3)]
452
453 if series_amplitude[HORZ]:
454 self.horizontal_step = float (self.plot_width) / series_amplitude[HORZ]
455 else:
456 self.horizontal_step = 0.00
457
458 if series_amplitude[VERT]:
459 self.vertical_step = float (self.plot_height) / series_amplitude[VERT]
460 else:
461 self.vertical_step = 0.00
462
463 if series_amplitude[NORM]:
464 if self.variable_radius:
465 self.z_step = float (self.bounds[NORM][1]) / series_amplitude[NORM]
466 if self.circle_colors:
467 self.circle_color_step = tuple([float(self.circle_colors[1][i]-self.circle_colors[0][i])/series_amplitude[NORM] for i in range(4)])
468 else:
469 self.z_step = 0.00
470 self.circle_color_step = ( 0.0, 0.0, 0.0, 0.0 )
471
472 def get_circle_color(self, value):
473 return tuple( [self.circle_colors[0][i] + value*self.circle_color_step[i] for i in range(4)] )
474
475 def render(self):
476 self.calc_all_extents()
477 self.calc_steps()
478 self.render_background()
479 self.render_bounding_box()
480 if self.axis:
481 self.render_axis()
482 if self.grid:
483 self.render_grid()
484 self.render_labels()
485 self.render_plot()
486 if self.errors:
487 self.render_errors()
488 if self.series_legend and self.series_labels:
489 self.render_legend()
490
491 def render_axis(self):
492 #Draws both the axis lines and their titles
493 cr = self.context
494 cr.set_source_rgba(*self.line_color)
495 cr.move_to(self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT])
496 cr.line_to(self.borders[HORZ], self.borders[VERT])
497 cr.stroke()
498
499 cr.move_to(self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT])
500 cr.line_to(self.dimensions[HORZ] - self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT])
501 cr.stroke()
502
503 cr.set_source_rgba(*self.label_color)
504 self.context.set_font_size( 1.2 * self.font_size )
505 if self.titles[HORZ]:
506 title_width,title_height = cr.text_extents(self.titles[HORZ])[2:4]
507 cr.move_to( self.dimensions[HORZ]/2 - title_width/2, self.borders[VERT] - title_height/2 )
508 cr.show_text( self.titles[HORZ] )
509
510 if self.titles[VERT]:
511 title_width,title_height = cr.text_extents(self.titles[VERT])[2:4]
512 cr.move_to( self.dimensions[HORZ] - self.borders[HORZ] + title_height/2, self.dimensions[VERT]/2 - title_width/2)
513 cr.save()
514 cr.rotate( math.pi/2 )
515 cr.show_text( self.titles[VERT] )
516 cr.restore()
517
518 def render_grid(self):
519 cr = self.context
520 horizontal_step = float( self.plot_height ) / ( len( self.labels[VERT] ) - 1 )
521 vertical_step = float( self.plot_width ) / ( len( self.labels[HORZ] ) - 1 )
522
523 x = self.borders[HORZ] + vertical_step
524 y = self.plot_top - horizontal_step
525
526 for label in self.labels[HORZ][:-1]:
527 cr.set_source_rgba(*self.grid_color)
528 cr.move_to(x, self.dimensions[VERT] - self.borders[VERT])
529 cr.line_to(x, self.borders[VERT])
530 cr.stroke()
531 x += vertical_step
532 for label in self.labels[VERT][:-1]:
533 cr.set_source_rgba(*self.grid_color)
534 cr.move_to(self.borders[HORZ], y)
535 cr.line_to(self.dimensions[HORZ] - self.borders[HORZ], y)
536 cr.stroke()
537 y -= horizontal_step
538
539 def render_labels(self):
540 self.context.set_font_size(self.font_size * 0.8)
541 self.render_horz_labels()
542 self.render_vert_labels()
543
544 def render_horz_labels(self):
545 cr = self.context
546 step = float( self.plot_width ) / ( len( self.labels[HORZ] ) - 1 )
547 x = self.borders[HORZ]
548 y = self.dimensions[VERT] - self.borders[VERT] + 5
549
550 # store rotation matrix from the initial state
551 rotation_matrix = cr.get_matrix()
552 rotation_matrix.rotate(self.x_label_angle)
553
554 cr.set_source_rgba(*self.label_color)
555
556 for item in self.labels[HORZ]:
557 width = cr.text_extents(item)[2]
558 cr.move_to(x, y)
559 cr.save()
560 cr.set_matrix(rotation_matrix)
561 cr.show_text(item)
562 cr.restore()
563 x += step
564
565 def render_vert_labels(self):
566 cr = self.context
567 step = ( self.plot_height ) / ( len( self.labels[VERT] ) - 1 )
568 y = self.plot_top
569 cr.set_source_rgba(*self.label_color)
570 for item in self.labels[VERT]:
571 width = cr.text_extents(item)[2]
572 cr.move_to(self.borders[HORZ] - width - 5,y)
573 cr.show_text(item)
574 y -= step
575
576 def render_legend(self):
577 cr = self.context
578 cr.set_font_size(self.font_size)
579 cr.set_line_width(self.line_width)
580
581 widest_word = max(self.series_labels, key = lambda item: self.context.text_extents(item)[2])
582 tallest_word = max(self.series_labels, key = lambda item: self.context.text_extents(item)[3])
583 max_width = self.context.text_extents(widest_word)[2]
584 max_height = self.context.text_extents(tallest_word)[3] * 1.1
585
586 color_box_height = max_height / 2
587 color_box_width = color_box_height * 2
588
589 #Draw a bounding box
590 bounding_box_width = max_width + color_box_width + 15
591 bounding_box_height = (len(self.series_labels)+0.5) * max_height
592 cr.set_source_rgba(1,1,1)
593 cr.rectangle(self.dimensions[HORZ] - self.borders[HORZ] - bounding_box_width, self.borders[VERT],
594 bounding_box_width, bounding_box_height)
595 cr.fill()
596
597 cr.set_source_rgba(*self.line_color)
598 cr.set_line_width(self.line_width)
599 cr.rectangle(self.dimensions[HORZ] - self.borders[HORZ] - bounding_box_width, self.borders[VERT],
600 bounding_box_width, bounding_box_height)
601 cr.stroke()
602
603 for idx,key in enumerate(self.series_labels):
604 #Draw color box
605 cr.set_source_rgba(*self.series_colors[idx][:4])
606 cr.rectangle(self.dimensions[HORZ] - self.borders[HORZ] - max_width - color_box_width - 10,
607 self.borders[VERT] + color_box_height + (idx*max_height) ,
608 color_box_width, color_box_height)
609 cr.fill()
610
611 cr.set_source_rgba(0, 0, 0)
612 cr.rectangle(self.dimensions[HORZ] - self.borders[HORZ] - max_width - color_box_width - 10,
613 self.borders[VERT] + color_box_height + (idx*max_height),
614 color_box_width, color_box_height)
615 cr.stroke()
616
617 #Draw series labels
618 cr.set_source_rgba(0, 0, 0)
619 cr.move_to(self.dimensions[HORZ] - self.borders[HORZ] - max_width - 5, self.borders[VERT] + ((idx+1)*max_height))
620 cr.show_text(key)
621
622 def render_errors(self):
623 cr = self.context
624 cr.rectangle(self.borders[HORZ], self.borders[VERT], self.plot_width, self.plot_height)
625 cr.clip()
626 radius = self.dots
627 x0 = self.borders[HORZ] - self.bounds[HORZ][0]*self.horizontal_step
628 y0 = self.borders[VERT] - self.bounds[VERT][0]*self.vertical_step
629 for index, group in enumerate(self.series):
630 cr.set_source_rgba(*self.series_colors[index][:4])
631 for number, data in enumerate(group):
632 x = x0 + self.horizontal_step * data.content[0]
633 y = self.dimensions[VERT] - y0 - self.vertical_step * data.content[1]
634 if self.errors[HORZ]:
635 cr.move_to(x, y)
636 x1 = x - self.horizontal_step * self.errors[HORZ][0][number]
637 cr.line_to(x1, y)
638 cr.line_to(x1, y - radius)
639 cr.line_to(x1, y + radius)
640 cr.stroke()
641 if self.errors[HORZ] and len(self.errors[HORZ]) == 2:
642 cr.move_to(x, y)
643 x1 = x + self.horizontal_step * self.errors[HORZ][1][number]
644 cr.line_to(x1, y)
645 cr.line_to(x1, y - radius)
646 cr.line_to(x1, y + radius)
647 cr.stroke()
648 if self.errors[VERT]:
649 cr.move_to(x, y)
650 y1 = y + self.vertical_step * self.errors[VERT][0][number]
651 cr.line_to(x, y1)
652 cr.line_to(x - radius, y1)
653 cr.line_to(x + radius, y1)
654 cr.stroke()
655 if self.errors[VERT] and len(self.errors[VERT]) == 2:
656 cr.move_to(x, y)
657 y1 = y - self.vertical_step * self.errors[VERT][1][number]
658 cr.line_to(x, y1)
659 cr.line_to(x - radius, y1)
660 cr.line_to(x + radius, y1)
661 cr.stroke()
662
663
664 def render_plot(self):
665 cr = self.context
666 if self.discrete:
667 cr.rectangle(self.borders[HORZ], self.borders[VERT], self.plot_width, self.plot_height)
668 cr.clip()
669 x0 = self.borders[HORZ] - self.bounds[HORZ][0]*self.horizontal_step
670 y0 = self.borders[VERT] - self.bounds[VERT][0]*self.vertical_step
671 radius = self.dots
672 for number, group in enumerate (self.series):
673 cr.set_source_rgba(*self.series_colors[number][:4])
674 for data in group :
675 if self.variable_radius:
676 radius = data.content[2]*self.z_step
677 if self.circle_colors:
678 cr.set_source_rgba( *self.get_circle_color( data.content[2]) )
679 x = x0 + self.horizontal_step*data.content[0]
680 y = y0 + self.vertical_step*data.content[1]
681 cr.arc(x, self.dimensions[VERT] - y, radius, 0, 2*math.pi)
682 cr.fill()
683 else:
684 cr.rectangle(self.borders[HORZ], self.borders[VERT], self.plot_width, self.plot_height)
685 cr.clip()
686 x0 = self.borders[HORZ] - self.bounds[HORZ][0]*self.horizontal_step
687 y0 = self.borders[VERT] - self.bounds[VERT][0]*self.vertical_step
688 radius = self.dots
689 for number, group in enumerate (self.series):
690 last_data = None
691 cr.set_source_rgba(*self.series_colors[number][:4])
692 for data in group :
693 x = x0 + self.horizontal_step*data.content[0]
694 y = y0 + self.vertical_step*data.content[1]
695 if self.dots:
696 if self.variable_radius:
697 radius = data.content[2]*self.z_step
698 cr.arc(x, self.dimensions[VERT] - y, radius, 0, 2*math.pi)
699 cr.fill()
700 if last_data :
701 old_x = x0 + self.horizontal_step*last_data.content[0]
702 old_y = y0 + self.vertical_step*last_data.content[1]
703 cr.move_to( old_x, self.dimensions[VERT] - old_y )
704 cr.line_to( x, self.dimensions[VERT] - y)
705 cr.set_line_width(self.series_widths[number])
706
707 # Display line as dash line
708 if self.dash and self.dash[number]:
709 s = self.series_widths[number]
710 cr.set_dash([s*3, s*3], 0)
711
712 cr.stroke()
713 cr.set_dash([])
714 last_data = data
715
716class DotLinePlot(ScatterPlot):
717 def __init__(self,
718 surface=None,
719 data=None,
720 width=640,
721 height=480,
722 background=None,
723 border=0,
724 axis = False,
725 dash = False,
726 dots = 0,
727 grid = False,
728 series_legend = False,
729 x_labels = None,
730 y_labels = None,
731 x_bounds = None,
732 y_bounds = None,
733 x_title = None,
734 y_title = None,
735 series_colors = None):
736
737 ScatterPlot.__init__(self, surface, data, None, None, width, height, background, border,
738 axis, dash, False, dots, grid, series_legend, x_labels, y_labels,
739 x_bounds, y_bounds, None, x_title, y_title, series_colors, None )
740
741
742 def load_series(self, data, x_labels = None, y_labels = None, series_colors=None):
743 Plot.load_series(self, data, x_labels, y_labels, series_colors)
744 for group in self.series :
745 for index,data in enumerate(group):
746 group[index].content = (index, data.content)
747
748 self.calc_boundaries()
749 self.calc_labels()
750
751class FunctionPlot(ScatterPlot):
752 def __init__(self,
753 surface=None,
754 data=None,
755 width=640,
756 height=480,
757 background=None,
758 border=0,
759 axis = False,
760 discrete = False,
761 dots = 0,
762 grid = False,
763 series_legend = False,
764 x_labels = None,
765 y_labels = None,
766 x_bounds = None,
767 y_bounds = None,
768 x_title = None,
769 y_title = None,
770 series_colors = None,
771 step = 1):
772
773 self.function = data
774 self.step = step
775 self.discrete = discrete
776
777 data, x_bounds = self.load_series_from_function( self.function, x_bounds )
778
779 ScatterPlot.__init__(self, surface, data, None, None, width, height, background, border,
780 axis, False, discrete, dots, grid, series_legend, x_labels, y_labels,
781 x_bounds, y_bounds, None, x_title, y_title, series_colors, None )
782
783 def load_series(self, data, x_labels = None, y_labels = None, series_colors=None):
784 Plot.load_series(self, data, x_labels, y_labels, series_colors)
785
786 if len(self.series[0][0]) is 1:
787 for group_id, group in enumerate(self.series) :
788 for index,data in enumerate(group):
789 group[index].content = (self.bounds[HORZ][0] + self.step*index, data.content)
790
791 self.calc_boundaries()
792 self.calc_labels()
793
794 def load_series_from_function( self, function, x_bounds ):
795 #TODO: Add the possibility for the user to define multiple functions with different discretization parameters
796
797 #This function converts a function, a list of functions or a dictionary
798 #of functions into its corresponding array of data
799 series = Series()
800
801 if isinstance(function, Group) or isinstance(function, Data):
802 function = Series(function)
803
804 # If is instance of Series
805 if isinstance(function, Series):
806 # Overwrite any bounds passed by the function
807 x_bounds = (function.range[0],function.range[-1])
808
809 #if no bounds are provided
810 if x_bounds == None:
811 x_bounds = (0,10)
812
813
814 #TODO: Finish the dict translation
815 if hasattr(function, "keys"): #dictionary:
816 for key in function.keys():
817 group = Group(name=key)
818 #data[ key ] = []
819 i = x_bounds[0]
820 while i <= x_bounds[1] :
821 group.add_data(function[ key ](i))
822 #data[ key ].append( function[ key ](i) )
823 i += self.step
824 series.add_group(group)
825
826 elif hasattr(function, "__delitem__"): #list of functions
827 for index,f in enumerate( function ) :
828 group = Group()
829 #data.append( [] )
830 i = x_bounds[0]
831 while i <= x_bounds[1] :
832 group.add_data(f(i))
833 #data[ index ].append( f(i) )
834 i += self.step
835 series.add_group(group)
836
837 elif isinstance(function, Series): # instance of Series
838 series = function
839
840 else: #function
841 group = Group()
842 i = x_bounds[0]
843 while i <= x_bounds[1] :
844 group.add_data(function(i))
845 i += self.step
846 series.add_group(group)
847
848
849 return series, x_bounds
850
851 def calc_labels(self):
852 if not self.labels[HORZ]:
853 self.labels[HORZ] = []
854 i = self.bounds[HORZ][0]
855 while i<=self.bounds[HORZ][1]:
856 self.labels[HORZ].append(str(i))
857 i += float(self.bounds[HORZ][1] - self.bounds[HORZ][0])/10
858 ScatterPlot.calc_labels(self)
859
860 def render_plot(self):
861 if not self.discrete:
862 ScatterPlot.render_plot(self)
863 else:
864 last = None
865 cr = self.context
866 for number, group in enumerate (self.series):
867 cr.set_source_rgba(*self.series_colors[number][:4])
868 x0 = self.borders[HORZ] - self.bounds[HORZ][0]*self.horizontal_step
869 y0 = self.borders[VERT] - self.bounds[VERT][0]*self.vertical_step
870 for data in group:
871 x = x0 + self.horizontal_step * data.content[0]
872 y = y0 + self.vertical_step * data.content[1]
873 cr.move_to(x, self.dimensions[VERT] - y)
874 cr.line_to(x, self.plot_top)
875 cr.set_line_width(self.series_widths[number])
876 cr.stroke()
877 if self.dots:
878 cr.new_path()
879 cr.arc(x, self.dimensions[VERT] - y, 3, 0, 2.1 * math.pi)
880 cr.close_path()
881 cr.fill()
882
883class BarPlot(Plot):
884 def __init__(self,
885 surface = None,
886 data = None,
887 width = 640,
888 height = 480,
889 background = "white light_gray",
890 border = 0,
891 display_values = False,
892 grid = False,
893 rounded_corners = False,
894 stack = False,
895 three_dimension = False,
896 x_labels = None,
897 y_labels = None,
898 x_bounds = None,
899 y_bounds = None,
900 series_colors = None,
901 main_dir = None):
902
903 self.bounds = {}
904 self.bounds[HORZ] = x_bounds
905 self.bounds[VERT] = y_bounds
906 self.display_values = display_values
907 self.grid = grid
908 self.rounded_corners = rounded_corners
909 self.stack = stack
910 self.three_dimension = three_dimension
911 self.x_label_angle = math.pi / 2.5
912 self.main_dir = main_dir
913 self.max_value = {}
914 self.plot_dimensions = {}
915 self.steps = {}
916 self.value_label_color = (0.5,0.5,0.5,1.0)
917
918 Plot.__init__(self, surface, data, width, height, background, border, x_labels, y_labels, series_colors)
919
920 def load_series(self, data, x_labels = None, y_labels = None, series_colors = None):
921 Plot.load_series(self, data, x_labels, y_labels, series_colors)
922 self.calc_boundaries()
923
924 def process_colors(self, series_colors):
925 #Data for a BarPlot might be a List or a List of Lists.
926 #On the first case, colors must be generated for all bars,
927 #On the second, colors must be generated for each of the inner lists.
928
929 #TODO: Didn't get it...
930 #if hasattr(self.data[0], '__getitem__'):
931 # length = max(len(series) for series in self.data)
932 #else:
933 # length = len( self.data )
934
935 length = max(len(group) for group in self.series)
936
937 Plot.process_colors( self, series_colors, length, 'linear')
938
939 def calc_boundaries(self):
940 if not self.bounds[self.main_dir]:
941 if self.stack:
942 max_data_value = max(sum(group.to_list()) for group in self.series)
943 else:
944 max_data_value = max(max(group.to_list()) for group in self.series)
945 self.bounds[self.main_dir] = (0, max_data_value)
946 if not self.bounds[other_direction(self.main_dir)]:
947 self.bounds[other_direction(self.main_dir)] = (0, len(self.series))
948
949 def calc_extents(self, direction):
950 self.max_value[direction] = 0
951 if self.labels[direction]:
952 widest_word = max(self.labels[direction], key = lambda item: self.context.text_extents(item)[2])
953 self.max_value[direction] = self.context.text_extents(widest_word)[3 - direction]
954 self.borders[other_direction(direction)] = (2-direction)*self.max_value[direction] + self.border + direction*(5)
955 else:
956 self.borders[other_direction(direction)] = self.border
957
958 def calc_horz_extents(self):
959 self.calc_extents(HORZ)
960
961 def calc_vert_extents(self):
962 self.calc_extents(VERT)
963
964 def calc_all_extents(self):
965 self.calc_horz_extents()
966 self.calc_vert_extents()
967 other_dir = other_direction(self.main_dir)
968 self.value_label = 0
969 if self.display_values:
970 if self.stack:
971 self.value_label = self.context.text_extents(str(max(sum(group.to_list()) for group in self.series)))[2 + self.main_dir]
972 else:
973 self.value_label = self.context.text_extents(str(max(max(group.to_list()) for group in self.series)))[2 + self.main_dir]
974 if self.labels[self.main_dir]:
975 self.plot_dimensions[self.main_dir] = self.dimensions[self.main_dir] - 2*self.borders[self.main_dir] - self.value_label
976 else:
977 self.plot_dimensions[self.main_dir] = self.dimensions[self.main_dir] - self.borders[self.main_dir] - 1.2*self.border - self.value_label
978 self.plot_dimensions[other_dir] = self.dimensions[other_dir] - self.borders[other_dir] - self.border
979 self.plot_top = self.dimensions[VERT] - self.borders[VERT]
980
981 def calc_steps(self):
982 other_dir = other_direction(self.main_dir)
983 self.series_amplitude = self.bounds[self.main_dir][1] - self.bounds[self.main_dir][0]
984 if self.series_amplitude:
985 self.steps[self.main_dir] = float(self.plot_dimensions[self.main_dir])/self.series_amplitude
986 else:
987 self.steps[self.main_dir] = 0.00
988 series_length = len(self.series)
989 self.steps[other_dir] = float(self.plot_dimensions[other_dir])/(series_length + 0.1*(series_length + 1))
990 self.space = 0.1*self.steps[other_dir]
991
992 def render(self):
993 self.calc_all_extents()
994 self.calc_steps()
995 self.render_background()
996 self.render_bounding_box()
997 if self.grid:
998 self.render_grid()
999 if self.three_dimension:
1000 self.render_ground()
1001 if self.display_values:
1002 self.render_values()
1003 self.render_labels()
1004 self.render_plot()
1005 if self.series_labels:
1006 self.render_legend()
1007
1008 def draw_3d_rectangle_front(self, x0, y0, x1, y1, shift):
1009 self.context.rectangle(x0-shift, y0+shift, x1-x0, y1-y0)
1010
1011 def draw_3d_rectangle_side(self, x0, y0, x1, y1, shift):
1012 self.context.move_to(x1-shift,y0+shift)
1013 self.context.line_to(x1, y0)
1014 self.context.line_to(x1, y1)
1015 self.context.line_to(x1-shift, y1+shift)
1016 self.context.line_to(x1-shift, y0+shift)
1017 self.context.close_path()
1018
1019 def draw_3d_rectangle_top(self, x0, y0, x1, y1, shift):
1020 self.context.move_to(x0-shift,y0+shift)
1021 self.context.line_to(x0, y0)
1022 self.context.line_to(x1, y0)
1023 self.context.line_to(x1-shift, y0+shift)
1024 self.context.line_to(x0-shift, y0+shift)
1025 self.context.close_path()
1026
1027 def draw_round_rectangle(self, x0, y0, x1, y1):
1028 self.context.arc(x0+5, y0+5, 5, -math.pi, -math.pi/2)
1029 self.context.line_to(x1-5, y0)
1030 self.context.arc(x1-5, y0+5, 5, -math.pi/2, 0)
1031 self.context.line_to(x1, y1-5)
1032 self.context.arc(x1-5, y1-5, 5, 0, math.pi/2)
1033 self.context.line_to(x0+5, y1)
1034 self.context.arc(x0+5, y1-5, 5, math.pi/2, math.pi)
1035 self.context.line_to(x0, y0+5)
1036 self.context.close_path()
1037
1038 def render_ground(self):
1039 self.draw_3d_rectangle_front(self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT],
1040 self.dimensions[HORZ] - self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT] + 5, 10)
1041 self.context.fill()
1042
1043 self.draw_3d_rectangle_side (self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT],
1044 self.dimensions[HORZ] - self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT] + 5, 10)
1045 self.context.fill()
1046
1047 self.draw_3d_rectangle_top (self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT],
1048 self.dimensions[HORZ] - self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT] + 5, 10)
1049 self.context.fill()
1050
1051 def render_labels(self):
1052 self.context.set_font_size(self.font_size * 0.8)
1053 if self.labels[HORZ]:
1054 self.render_horz_labels()
1055 if self.labels[VERT]:
1056 self.render_vert_labels()
1057
1058 def render_legend(self):
1059 cr = self.context
1060 cr.set_font_size(self.font_size)
1061 cr.set_line_width(self.line_width)
1062
1063 widest_word = max(self.series_labels, key = lambda item: self.context.text_extents(item)[2])
1064 tallest_word = max(self.series_labels, key = lambda item: self.context.text_extents(item)[3])
1065 max_width = self.context.text_extents(widest_word)[2]
1066 max_height = self.context.text_extents(tallest_word)[3] * 1.1 + 5
1067
1068 color_box_height = max_height / 2
1069 color_box_width = color_box_height * 2
1070
1071 #Draw a bounding box
1072 bounding_box_width = max_width + color_box_width + 15
1073 bounding_box_height = (len(self.series_labels)+0.5) * max_height
1074 cr.set_source_rgba(1,1,1)
1075 cr.rectangle(self.dimensions[HORZ] - self.border - bounding_box_width, self.border,
1076 bounding_box_width, bounding_box_height)
1077 cr.fill()
1078
1079 cr.set_source_rgba(*self.line_color)
1080 cr.set_line_width(self.line_width)
1081 cr.rectangle(self.dimensions[HORZ] - self.border - bounding_box_width, self.border,
1082 bounding_box_width, bounding_box_height)
1083 cr.stroke()
1084
1085 for idx,key in enumerate(self.series_labels):
1086 #Draw color box
1087 cr.set_source_rgba(*self.series_colors[idx][:4])
1088 cr.rectangle(self.dimensions[HORZ] - self.border - max_width - color_box_width - 10,
1089 self.border + color_box_height + (idx*max_height) ,
1090 color_box_width, color_box_height)
1091 cr.fill()
1092
1093 cr.set_source_rgba(0, 0, 0)
1094 cr.rectangle(self.dimensions[HORZ] - self.border - max_width - color_box_width - 10,
1095 self.border + color_box_height + (idx*max_height),
1096 color_box_width, color_box_height)
1097 cr.stroke()
1098
1099 #Draw series labels
1100 cr.set_source_rgba(0, 0, 0)
1101 cr.move_to(self.dimensions[HORZ] - self.border - max_width - 5, self.border + ((idx+1)*max_height))
1102 cr.show_text(key)
1103
1104
1105class HorizontalBarPlot(BarPlot):
1106 def __init__(self,
1107 surface = None,
1108 data = None,
1109 width = 640,
1110 height = 480,
1111 background = "white light_gray",
1112 border = 0,
1113 display_values = False,
1114 grid = False,
1115 rounded_corners = False,
1116 stack = False,
1117 three_dimension = False,
1118 series_labels = None,
1119 x_labels = None,
1120 y_labels = None,
1121 x_bounds = None,
1122 y_bounds = None,
1123 series_colors = None):
1124
1125 BarPlot.__init__(self, surface, data, width, height, background, border,
1126 display_values, grid, rounded_corners, stack, three_dimension,
1127 x_labels, y_labels, x_bounds, y_bounds, series_colors, HORZ)
1128 self.series_labels = series_labels
1129
1130 def calc_vert_extents(self):
1131 self.calc_extents(VERT)
1132 if self.labels[HORZ] and not self.labels[VERT]:
1133 self.borders[HORZ] += 10
1134
1135 def draw_rectangle_bottom(self, x0, y0, x1, y1):
1136 self.context.arc(x0+5, y1-5, 5, math.pi/2, math.pi)
1137 self.context.line_to(x0, y0+5)
1138 self.context.arc(x0+5, y0+5, 5, -math.pi, -math.pi/2)
1139 self.context.line_to(x1, y0)
1140 self.context.line_to(x1, y1)
1141 self.context.line_to(x0+5, y1)
1142 self.context.close_path()
1143
1144 def draw_rectangle_top(self, x0, y0, x1, y1):
1145 self.context.arc(x1-5, y0+5, 5, -math.pi/2, 0)
1146 self.context.line_to(x1, y1-5)
1147 self.context.arc(x1-5, y1-5, 5, 0, math.pi/2)
1148 self.context.line_to(x0, y1)
1149 self.context.line_to(x0, y0)
1150 self.context.line_to(x1, y0)
1151 self.context.close_path()
1152
1153 def draw_rectangle(self, index, length, x0, y0, x1, y1):
1154 if length == 1:
1155 BarPlot.draw_rectangle(self, x0, y0, x1, y1)
1156 elif index == 0:
1157 self.draw_rectangle_bottom(x0, y0, x1, y1)
1158 elif index == length-1:
1159 self.draw_rectangle_top(x0, y0, x1, y1)
1160 else:
1161 self.context.rectangle(x0, y0, x1-x0, y1-y0)
1162
1163 #TODO: Review BarPlot.render_grid code
1164 def render_grid(self):
1165 self.context.set_source_rgba(0.8, 0.8, 0.8)
1166 if self.labels[HORZ]:
1167 self.context.set_font_size(self.font_size * 0.8)
1168 step = (self.dimensions[HORZ] - 2*self.borders[HORZ] - self.value_label)/(len(self.labels[HORZ])-1)
1169 x = self.borders[HORZ]
1170 next_x = 0
1171 for item in self.labels[HORZ]:
1172 width = self.context.text_extents(item)[2]
1173 if x - width/2 > next_x and x - width/2 > self.border:
1174 self.context.move_to(x, self.border)
1175 self.context.line_to(x, self.dimensions[VERT] - self.borders[VERT])
1176 self.context.stroke()
1177 next_x = x + width/2
1178 x += step
1179 else:
1180 lines = 11
1181 horizontal_step = float(self.plot_dimensions[HORZ])/(lines-1)
1182 x = self.borders[HORZ]
1183 for y in xrange(0, lines):
1184 self.context.move_to(x, self.border)
1185 self.context.line_to(x, self.dimensions[VERT] - self.borders[VERT])
1186 self.context.stroke()
1187 x += horizontal_step
1188
1189 def render_horz_labels(self):
1190 step = (self.dimensions[HORZ] - 2*self.borders[HORZ])/(len(self.labels[HORZ])-1)
1191 x = self.borders[HORZ]
1192 next_x = 0
1193
1194 for item in self.labels[HORZ]:
1195 self.context.set_source_rgba(*self.label_color)
1196 width = self.context.text_extents(item)[2]
1197 if x - width/2 > next_x and x - width/2 > self.border:
1198 self.context.move_to(x - width/2, self.dimensions[VERT] - self.borders[VERT] + self.max_value[HORZ] + 3)
1199 self.context.show_text(item)
1200 next_x = x + width/2
1201 x += step
1202
1203 def render_vert_labels(self):
1204 series_length = len(self.labels[VERT])
1205 step = (self.plot_dimensions[VERT] - (series_length + 1)*self.space)/(len(self.labels[VERT]))
1206 y = self.border + step/2 + self.space
1207
1208 for item in self.labels[VERT]:
1209 self.context.set_source_rgba(*self.label_color)
1210 width, height = self.context.text_extents(item)[2:4]
1211 self.context.move_to(self.borders[HORZ] - width - 5, y + height/2)
1212 self.context.show_text(item)
1213 y += step + self.space
1214 self.labels[VERT].reverse()
1215
1216 def render_values(self):
1217 self.context.set_source_rgba(*self.value_label_color)
1218 self.context.set_font_size(self.font_size * 0.8)
1219 if self.stack:
1220 for i,group in enumerate(self.series):
1221 value = sum(group.to_list())
1222 height = self.context.text_extents(str(value))[3]
1223 x = self.borders[HORZ] + value*self.steps[HORZ] + 2
1224 y = self.borders[VERT] + (i+0.5)*self.steps[VERT] + (i+1)*self.space + height/2
1225 self.context.move_to(x, y)
1226 self.context.show_text(str(value))
1227 else:
1228 for i,group in enumerate(self.series):
1229 inner_step = self.steps[VERT]/len(group)
1230 y0 = self.border + i*self.steps[VERT] + (i+1)*self.space
1231 for number,data in enumerate(group):
1232 height = self.context.text_extents(str(data.content))[3]
1233 self.context.move_to(self.borders[HORZ] + data.content*self.steps[HORZ] + 2, y0 + 0.5*inner_step + height/2, )
1234 self.context.show_text(str(data.content))
1235 y0 += inner_step
1236
1237 def render_plot(self):
1238 if self.stack:
1239 for i,group in enumerate(self.series):
1240 x0 = self.borders[HORZ]
1241 y0 = self.borders[VERT] + i*self.steps[VERT] + (i+1)*self.space
1242 for number,data in enumerate(group):
1243 if self.series_colors[number][4] in ('radial','linear') :
1244 linear = cairo.LinearGradient( data.content*self.steps[HORZ]/2, y0, data.content*self.steps[HORZ]/2, y0 + self.steps[VERT] )
1245 color = self.series_colors[number]
1246 linear.add_color_stop_rgba(0.0, 3.5*color[0]/5.0, 3.5*color[1]/5.0, 3.5*color[2]/5.0,1.0)
1247 linear.add_color_stop_rgba(1.0, *color[:4])
1248 self.context.set_source(linear)
1249 elif self.series_colors[number][4] == 'solid':
1250 self.context.set_source_rgba(*self.series_colors[number][:4])
1251 if self.rounded_corners:
1252 self.draw_rectangle(number, len(group), x0, y0, x0+data.content*self.steps[HORZ], y0+self.steps[VERT])
1253 self.context.fill()
1254 else:
1255 self.context.rectangle(x0, y0, data.content*self.steps[HORZ], self.steps[VERT])
1256 self.context.fill()
1257 x0 += data.content*self.steps[HORZ]
1258 else:
1259 for i,group in enumerate(self.series):
1260 inner_step = self.steps[VERT]/len(group)
1261 x0 = self.borders[HORZ]
1262 y0 = self.border + i*self.steps[VERT] + (i+1)*self.space
1263 for number,data in enumerate(group):
1264 linear = cairo.LinearGradient(data.content*self.steps[HORZ]/2, y0, data.content*self.steps[HORZ]/2, y0 + inner_step)
1265 color = self.series_colors[number]
1266 linear.add_color_stop_rgba(0.0, 3.5*color[0]/5.0, 3.5*color[1]/5.0, 3.5*color[2]/5.0,1.0)
1267 linear.add_color_stop_rgba(1.0, *color[:4])
1268 self.context.set_source(linear)
1269 if self.rounded_corners and data.content != 0:
1270 BarPlot.draw_round_rectangle(self,x0, y0, x0 + data.content*self.steps[HORZ], y0 + inner_step)
1271 self.context.fill()
1272 else:
1273 self.context.rectangle(x0, y0, data.content*self.steps[HORZ], inner_step)
1274 self.context.fill()
1275 y0 += inner_step
1276
1277class VerticalBarPlot(BarPlot):
1278 def __init__(self,
1279 surface = None,
1280 data = None,
1281 width = 640,
1282 height = 480,
1283 background = "white light_gray",
1284 border = 0,
1285 display_values = False,
1286 grid = False,
1287 rounded_corners = False,
1288 stack = False,
1289 three_dimension = False,
1290 series_labels = None,
1291 x_labels = None,
1292 y_labels = None,
1293 x_bounds = None,
1294 y_bounds = None,
1295 series_colors = None):
1296
1297 BarPlot.__init__(self, surface, data, width, height, background, border,
1298 display_values, grid, rounded_corners, stack, three_dimension,
1299 x_labels, y_labels, x_bounds, y_bounds, series_colors, VERT)
1300 self.series_labels = series_labels
1301
1302 def calc_vert_extents(self):
1303 self.calc_extents(VERT)
1304 if self.labels[VERT] and not self.labels[HORZ]:
1305 self.borders[VERT] += 10
1306
1307 def draw_rectangle_bottom(self, x0, y0, x1, y1):
1308 self.context.move_to(x1,y1)
1309 self.context.arc(x1-5, y1-5, 5, 0, math.pi/2)
1310 self.context.line_to(x0+5, y1)
1311 self.context.arc(x0+5, y1-5, 5, math.pi/2, math.pi)
1312 self.context.line_to(x0, y0)
1313 self.context.line_to(x1, y0)
1314 self.context.line_to(x1, y1)
1315 self.context.close_path()
1316
1317 def draw_rectangle_top(self, x0, y0, x1, y1):
1318 self.context.arc(x0+5, y0+5, 5, -math.pi, -math.pi/2)
1319 self.context.line_to(x1-5, y0)
1320 self.context.arc(x1-5, y0+5, 5, -math.pi/2, 0)
1321 self.context.line_to(x1, y1)
1322 self.context.line_to(x0, y1)
1323 self.context.line_to(x0, y0)
1324 self.context.close_path()
1325
1326 def draw_rectangle(self, index, length, x0, y0, x1, y1):
1327 if length == 1:
1328 BarPlot.draw_rectangle(self, x0, y0, x1, y1)
1329 elif index == 0:
1330 self.draw_rectangle_bottom(x0, y0, x1, y1)
1331 elif index == length-1:
1332 self.draw_rectangle_top(x0, y0, x1, y1)
1333 else:
1334 self.context.rectangle(x0, y0, x1-x0, y1-y0)
1335
1336 def render_grid(self):
1337 self.context.set_source_rgba(0.8, 0.8, 0.8)
1338 if self.labels[VERT]:
1339 lines = len(self.labels[VERT])
1340 vertical_step = float(self.plot_dimensions[self.main_dir])/(lines-1)
1341 y = self.borders[VERT] + self.value_label
1342 else:
1343 lines = 11
1344 vertical_step = float(self.plot_dimensions[self.main_dir])/(lines-1)
1345 y = 1.2*self.border + self.value_label
1346 for x in xrange(0, lines):
1347 self.context.move_to(self.borders[HORZ], y)
1348 self.context.line_to(self.dimensions[HORZ] - self.border, y)
1349 self.context.stroke()
1350 y += vertical_step
1351
1352 def render_ground(self):
1353 self.draw_3d_rectangle_front(self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT],
1354 self.dimensions[HORZ] - self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT] + 5, 10)
1355 self.context.fill()
1356
1357 self.draw_3d_rectangle_side (self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT],
1358 self.dimensions[HORZ] - self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT] + 5, 10)
1359 self.context.fill()
1360
1361 self.draw_3d_rectangle_top (self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT],
1362 self.dimensions[HORZ] - self.borders[HORZ], self.dimensions[VERT] - self.borders[VERT] + 5, 10)
1363 self.context.fill()
1364
1365 def render_horz_labels(self):
1366 series_length = len(self.labels[HORZ])
1367 step = float (self.plot_dimensions[HORZ] - (series_length + 1)*self.space)/len(self.labels[HORZ])
1368 x = self.borders[HORZ] + step/2 + self.space
1369 next_x = 0
1370
1371 for item in self.labels[HORZ]:
1372 self.context.set_source_rgba(*self.label_color)
1373 width = self.context.text_extents(item)[2]
1374 if x - width/2 > next_x and x - width/2 > self.borders[HORZ]:
1375 self.context.move_to(x - width/2, self.dimensions[VERT] - self.borders[VERT] + self.max_value[HORZ] + 3)
1376 self.context.show_text(item)
1377 next_x = x + width/2
1378 x += step + self.space
1379
1380 def render_vert_labels(self):
1381 self.context.set_source_rgba(*self.label_color)
1382 y = self.borders[VERT] + self.value_label
1383 step = (self.dimensions[VERT] - 2*self.borders[VERT] - self.value_label)/(len(self.labels[VERT]) - 1)
1384 self.labels[VERT].reverse()
1385 for item in self.labels[VERT]:
1386 width, height = self.context.text_extents(item)[2:4]
1387 self.context.move_to(self.borders[HORZ] - width - 5, y + height/2)
1388 self.context.show_text(item)
1389 y += step
1390 self.labels[VERT].reverse()
1391
1392 def render_values(self):
1393 self.context.set_source_rgba(*self.value_label_color)
1394 self.context.set_font_size(self.font_size * 0.8)
1395 if self.stack:
1396 for i,group in enumerate(self.series):
1397 value = sum(group.to_list())
1398 width = self.context.text_extents(str(value))[2]
1399 x = self.borders[HORZ] + (i+0.5)*self.steps[HORZ] + (i+1)*self.space - width/2
1400 y = value*self.steps[VERT] + 2
1401 self.context.move_to(x, self.plot_top-y)
1402 self.context.show_text(str(value))
1403 else:
1404 for i,group in enumerate(self.series):
1405 inner_step = self.steps[HORZ]/len(group)
1406 x0 = self.borders[HORZ] + i*self.steps[HORZ] + (i+1)*self.space
1407 for number,data in enumerate(group):
1408 width = self.context.text_extents(str(data.content))[2]
1409 self.context.move_to(x0 + 0.5*inner_step - width/2, self.plot_top - data.content*self.steps[VERT] - 2)
1410 self.context.show_text(str(data.content))
1411 x0 += inner_step
1412
1413 def render_plot(self):
1414 if self.stack:
1415 for i,group in enumerate(self.series):
1416 x0 = self.borders[HORZ] + i*self.steps[HORZ] + (i+1)*self.space
1417 y0 = 0
1418 for number,data in enumerate(group):
1419 if self.series_colors[number][4] in ('linear','radial'):
1420 linear = cairo.LinearGradient( x0, data.content*self.steps[VERT]/2, x0 + self.steps[HORZ], data.content*self.steps[VERT]/2 )
1421 color = self.series_colors[number]
1422 linear.add_color_stop_rgba(0.0, 3.5*color[0]/5.0, 3.5*color[1]/5.0, 3.5*color[2]/5.0,1.0)
1423 linear.add_color_stop_rgba(1.0, *color[:4])
1424 self.context.set_source(linear)
1425 elif self.series_colors[number][4] == 'solid':
1426 self.context.set_source_rgba(*self.series_colors[number][:4])
1427 if self.rounded_corners:
1428 self.draw_rectangle(number, len(group), x0, self.plot_top - y0 - data.content*self.steps[VERT], x0 + self.steps[HORZ], self.plot_top - y0)
1429 self.context.fill()
1430 else:
1431 self.context.rectangle(x0, self.plot_top - y0 - data.content*self.steps[VERT], self.steps[HORZ], data.content*self.steps[VERT])
1432 self.context.fill()
1433 y0 += data.content*self.steps[VERT]
1434 else:
1435 for i,group in enumerate(self.series):
1436 inner_step = self.steps[HORZ]/len(group)
1437 y0 = self.borders[VERT]
1438 x0 = self.borders[HORZ] + i*self.steps[HORZ] + (i+1)*self.space
1439 for number,data in enumerate(group):
1440 if self.series_colors[number][4] == 'linear':
1441 linear = cairo.LinearGradient( x0, data.content*self.steps[VERT]/2, x0 + inner_step, data.content*self.steps[VERT]/2 )
1442 color = self.series_colors[number]
1443 linear.add_color_stop_rgba(0.0, 3.5*color[0]/5.0, 3.5*color[1]/5.0, 3.5*color[2]/5.0,1.0)
1444 linear.add_color_stop_rgba(1.0, *color[:4])
1445 self.context.set_source(linear)
1446 elif self.series_colors[number][4] == 'solid':
1447 self.context.set_source_rgba(*self.series_colors[number][:4])
1448 if self.rounded_corners and data.content != 0:
1449 BarPlot.draw_round_rectangle(self, x0, self.plot_top - data.content*self.steps[VERT], x0+inner_step, self.plot_top)
1450 self.context.fill()
1451 elif self.three_dimension:
1452 self.draw_3d_rectangle_front(x0, self.plot_top - data.content*self.steps[VERT], x0+inner_step, self.plot_top, 5)
1453 self.context.fill()
1454 self.draw_3d_rectangle_side(x0, self.plot_top - data.content*self.steps[VERT], x0+inner_step, self.plot_top, 5)
1455 self.context.fill()
1456 self.draw_3d_rectangle_top(x0, self.plot_top - data.content*self.steps[VERT], x0+inner_step, self.plot_top, 5)
1457 self.context.fill()
1458 else:
1459 self.context.rectangle(x0, self.plot_top - data.content*self.steps[VERT], inner_step, data.content*self.steps[VERT])
1460 self.context.fill()
1461
1462 x0 += inner_step
1463
1464class StreamChart(VerticalBarPlot):
1465 def __init__(self,
1466 surface = None,
1467 data = None,
1468 width = 640,
1469 height = 480,
1470 background = "white light_gray",
1471 border = 0,
1472 grid = False,
1473 series_legend = None,
1474 x_labels = None,
1475 x_bounds = None,
1476 y_bounds = None,
1477 series_colors = None):
1478
1479 VerticalBarPlot.__init__(self, surface, data, width, height, background, border,
1480 False, grid, False, True, False,
1481 None, x_labels, None, x_bounds, y_bounds, series_colors)
1482
1483 def calc_steps(self):
1484 other_dir = other_direction(self.main_dir)
1485 self.series_amplitude = self.bounds[self.main_dir][1] - self.bounds[self.main_dir][0]
1486 if self.series_amplitude:
1487 self.steps[self.main_dir] = float(self.plot_dimensions[self.main_dir])/self.series_amplitude
1488 else:
1489 self.steps[self.main_dir] = 0.00
1490 series_length = len(self.data)
1491 self.steps[other_dir] = float(self.plot_dimensions[other_dir])/series_length
1492
1493 def render_legend(self):
1494 pass
1495
1496 def ground(self, index):
1497 sum_values = sum(self.data[index])
1498 return -0.5*sum_values
1499
1500 def calc_angles(self):
1501 middle = self.plot_top - self.plot_dimensions[VERT]/2.0
1502 self.angles = [tuple([0.0 for x in range(len(self.data)+1)])]
1503 for x_index in range(1, len(self.data)-1):
1504 t = []
1505 x0 = self.borders[HORZ] + (0.5 + x_index - 1)*self.steps[HORZ]
1506 x2 = self.borders[HORZ] + (0.5 + x_index + 1)*self.steps[HORZ]
1507 y0 = middle - self.ground(x_index-1)*self.steps[VERT]
1508 y2 = middle - self.ground(x_index+1)*self.steps[VERT]
1509 t.append(math.atan(float(y0-y2)/(x0-x2)))
1510 for data_index in range(len(self.data[x_index])):
1511 x0 = self.borders[HORZ] + (0.5 + x_index - 1)*self.steps[HORZ]
1512 x2 = self.borders[HORZ] + (0.5 + x_index + 1)*self.steps[HORZ]
1513 y0 = middle - self.ground(x_index-1)*self.steps[VERT] - self.data[x_index-1][data_index]*self.steps[VERT]
1514 y2 = middle - self.ground(x_index+1)*self.steps[VERT] - self.data[x_index+1][data_index]*self.steps[VERT]
1515
1516 for i in range(0,data_index):
1517 y0 -= self.data[x_index-1][i]*self.steps[VERT]
1518 y2 -= self.data[x_index+1][i]*self.steps[VERT]
1519
1520 if data_index == len(self.data[0])-1 and False:
1521 self.context.set_source_rgba(0.0,0.0,0.0,0.3)
1522 self.context.move_to(x0,y0)
1523 self.context.line_to(x2,y2)
1524 self.context.stroke()
1525 self.context.arc(x0,y0,2,0,2*math.pi)
1526 self.context.fill()
1527 t.append(math.atan(float(y0-y2)/(x0-x2)))
1528 self.angles.append(tuple(t))
1529 self.angles.append(tuple([0.0 for x in range(len(self.data)+1)]))
1530
1531 def render_plot(self):
1532 self.calc_angles()
1533 middle = self.plot_top - self.plot_dimensions[VERT]/2.0
1534 p = 0.4*self.steps[HORZ]
1535 for data_index in range(len(self.data[0])-1,-1,-1):
1536 self.context.set_source_rgba(*self.series_colors[data_index][:4])
1537
1538 #draw the upper line
1539 for x_index in range(len(self.data)-1) :
1540 x1 = self.borders[HORZ] + (0.5 + x_index)*self.steps[HORZ]
1541 y1 = middle - self.ground(x_index)*self.steps[VERT] - self.data[x_index][data_index]*self.steps[VERT]
1542 x2 = self.borders[HORZ] + (0.5 + x_index + 1)*self.steps[HORZ]
1543 y2 = middle - self.ground(x_index + 1)*self.steps[VERT] - self.data[x_index + 1][data_index]*self.steps[VERT]
1544
1545 for i in range(0,data_index):
1546 y1 -= self.data[x_index][i]*self.steps[VERT]
1547 y2 -= self.data[x_index+1][i]*self.steps[VERT]
1548
1549 if x_index == 0:
1550 self.context.move_to(x1,y1)
1551
1552 ang1 = self.angles[x_index][data_index+1]
1553 ang2 = self.angles[x_index+1][data_index+1] + math.pi
1554 self.context.curve_to(x1+p*math.cos(ang1),y1+p*math.sin(ang1),
1555 x2+p*math.cos(ang2),y2+p*math.sin(ang2),
1556 x2,y2)
1557
1558 for x_index in range(len(self.data)-1,0,-1) :
1559 x1 = self.borders[HORZ] + (0.5 + x_index)*self.steps[HORZ]
1560 y1 = middle - self.ground(x_index)*self.steps[VERT]
1561 x2 = self.borders[HORZ] + (0.5 + x_index - 1)*self.steps[HORZ]
1562 y2 = middle - self.ground(x_index - 1)*self.steps[VERT]
1563
1564 for i in range(0,data_index):
1565 y1 -= self.data[x_index][i]*self.steps[VERT]
1566 y2 -= self.data[x_index-1][i]*self.steps[VERT]
1567
1568 if x_index == len(self.data)-1:
1569 self.context.line_to(x1,y1+2)
1570
1571 #revert angles by pi degrees to take the turn back
1572 ang1 = self.angles[x_index][data_index] + math.pi
1573 ang2 = self.angles[x_index-1][data_index]
1574 self.context.curve_to(x1+p*math.cos(ang1),y1+p*math.sin(ang1),
1575 x2+p*math.cos(ang2),y2+p*math.sin(ang2),
1576 x2,y2+2)
1577
1578 self.context.close_path()
1579 self.context.fill()
1580
1581 if False:
1582 self.context.move_to(self.borders[HORZ] + 0.5*self.steps[HORZ], middle)
1583 for x_index in range(len(self.data)-1) :
1584 x1 = self.borders[HORZ] + (0.5 + x_index)*self.steps[HORZ]
1585 y1 = middle - self.ground(x_index)*self.steps[VERT] - self.data[x_index][data_index]*self.steps[VERT]
1586 x2 = self.borders[HORZ] + (0.5 + x_index + 1)*self.steps[HORZ]
1587 y2 = middle - self.ground(x_index + 1)*self.steps[VERT] - self.data[x_index + 1][data_index]*self.steps[VERT]
1588
1589 for i in range(0,data_index):
1590 y1 -= self.data[x_index][i]*self.steps[VERT]
1591 y2 -= self.data[x_index+1][i]*self.steps[VERT]
1592
1593 ang1 = self.angles[x_index][data_index+1]
1594 ang2 = self.angles[x_index+1][data_index+1] + math.pi
1595 self.context.set_source_rgba(1.0,0.0,0.0)
1596 self.context.arc(x1+p*math.cos(ang1),y1+p*math.sin(ang1),2,0,2*math.pi)
1597 self.context.fill()
1598 self.context.set_source_rgba(0.0,0.0,0.0)
1599 self.context.arc(x2+p*math.cos(ang2),y2+p*math.sin(ang2),2,0,2*math.pi)
1600 self.context.fill()
1601 '''self.context.set_source_rgba(0.0,0.0,0.0,0.3)
1602 self.context.arc(x2,y2,2,0,2*math.pi)
1603 self.context.fill()'''
1604 self.context.move_to(x1,y1)
1605 self.context.line_to(x1+p*math.cos(ang1),y1+p*math.sin(ang1))
1606 self.context.stroke()
1607 self.context.move_to(x2,y2)
1608 self.context.line_to(x2+p*math.cos(ang2),y2+p*math.sin(ang2))
1609 self.context.stroke()
1610 if False:
1611 for x_index in range(len(self.data)-1,0,-1) :
1612 x1 = self.borders[HORZ] + (0.5 + x_index)*self.steps[HORZ]
1613 y1 = middle - self.ground(x_index)*self.steps[VERT]
1614 x2 = self.borders[HORZ] + (0.5 + x_index - 1)*self.steps[HORZ]
1615 y2 = middle - self.ground(x_index - 1)*self.steps[VERT]
1616
1617 for i in range(0,data_index):
1618 y1 -= self.data[x_index][i]*self.steps[VERT]
1619 y2 -= self.data[x_index-1][i]*self.steps[VERT]
1620
1621 #revert angles by pi degrees to take the turn back
1622 ang1 = self.angles[x_index][data_index] + math.pi
1623 ang2 = self.angles[x_index-1][data_index]
1624 self.context.set_source_rgba(0.0,1.0,0.0)
1625 self.context.arc(x1+p*math.cos(ang1),y1+p*math.sin(ang1),2,0,2*math.pi)
1626 self.context.fill()
1627 self.context.set_source_rgba(0.0,0.0,1.0)
1628 self.context.arc(x2+p*math.cos(ang2),y2+p*math.sin(ang2),2,0,2*math.pi)
1629 self.context.fill()
1630 '''self.context.set_source_rgba(0.0,0.0,0.0,0.3)
1631 self.context.arc(x2,y2,2,0,2*math.pi)
1632 self.context.fill()'''
1633 self.context.move_to(x1,y1)
1634 self.context.line_to(x1+p*math.cos(ang1),y1+p*math.sin(ang1))
1635 self.context.stroke()
1636 self.context.move_to(x2,y2)
1637 self.context.line_to(x2+p*math.cos(ang2),y2+p*math.sin(ang2))
1638 self.context.stroke()
1639 #break
1640
1641 #self.context.arc(self.dimensions[HORZ]/2, self.dimensions[VERT]/2,50,0,3*math.pi/2)
1642 #self.context.fill()
1643
1644
1645class PiePlot(Plot):
1646 #TODO: Check the old cairoplot, graphs aren't matching
1647 def __init__ (self,
1648 surface = None,
1649 data = None,
1650 width = 640,
1651 height = 480,
1652 background = "white light_gray",
1653 gradient = False,
1654 shadow = False,
1655 colors = None):
1656
1657 Plot.__init__( self, surface, data, width, height, background, series_colors = colors )
1658 self.center = (self.dimensions[HORZ]/2, self.dimensions[VERT]/2)
1659 self.total = sum( self.series.to_list() )
1660 self.radius = min(self.dimensions[HORZ]/3,self.dimensions[VERT]/3)
1661 self.gradient = gradient
1662 self.shadow = shadow
1663
1664 def sort_function(x,y):
1665 return x.content - y.content
1666
1667 def load_series(self, data, x_labels=None, y_labels=None, series_colors=None):
1668 Plot.load_series(self, data, x_labels, y_labels, series_colors)
1669 # Already done inside series
1670 #self.data = sorted(self.data)
1671
1672 def draw_piece(self, angle, next_angle):
1673 self.context.move_to(self.center[0],self.center[1])
1674 self.context.line_to(self.center[0] + self.radius*math.cos(angle), self.center[1] + self.radius*math.sin(angle))
1675 self.context.arc(self.center[0], self.center[1], self.radius, angle, next_angle)
1676 self.context.line_to(self.center[0], self.center[1])
1677 self.context.close_path()
1678
1679 def render(self):
1680 self.render_background()
1681 self.render_bounding_box()
1682 if self.shadow:
1683 self.render_shadow()
1684 self.render_plot()
1685 self.render_series_labels()
1686
1687 def render_shadow(self):
1688 horizontal_shift = 3
1689 vertical_shift = 3
1690 self.context.set_source_rgba(0, 0, 0, 0.5)
1691 self.context.arc(self.center[0] + horizontal_shift, self.center[1] + vertical_shift, self.radius, 0, 2*math.pi)
1692 self.context.fill()
1693
1694 def render_series_labels(self):
1695 angle = 0
1696 next_angle = 0
1697 x0,y0 = self.center
1698 cr = self.context
1699 for number,key in enumerate(self.series_labels):
1700 # self.data[number] should be just a number
1701 data = sum(self.series[number].to_list())
1702
1703 next_angle = angle + 2.0*math.pi*data/self.total
1704 cr.set_source_rgba(*self.series_colors[number][:4])
1705 w = cr.text_extents(key)[2]
1706 if (angle + next_angle)/2 < math.pi/2 or (angle + next_angle)/2 > 3*math.pi/2:
1707 cr.move_to(x0 + (self.radius+10)*math.cos((angle+next_angle)/2), y0 + (self.radius+10)*math.sin((angle+next_angle)/2) )
1708 else:
1709 cr.move_to(x0 + (self.radius+10)*math.cos((angle+next_angle)/2) - w, y0 + (self.radius+10)*math.sin((angle+next_angle)/2) )
1710 cr.show_text(key)
1711 angle = next_angle
1712
1713 def render_plot(self):
1714 angle = 0
1715 next_angle = 0
1716 x0,y0 = self.center
1717 cr = self.context
1718 for number,group in enumerate(self.series):
1719 # Group should be just a number
1720 data = sum(group.to_list())
1721 next_angle = angle + 2.0*math.pi*data/self.total
1722 if self.gradient or self.series_colors[number][4] in ('linear','radial'):
1723 gradient_color = cairo.RadialGradient(self.center[0], self.center[1], 0, self.center[0], self.center[1], self.radius)
1724 gradient_color.add_color_stop_rgba(0.3, *self.series_colors[number][:4])
1725 gradient_color.add_color_stop_rgba(1, self.series_colors[number][0]*0.7,
1726 self.series_colors[number][1]*0.7,
1727 self.series_colors[number][2]*0.7,
1728 self.series_colors[number][3])
1729 cr.set_source(gradient_color)
1730 else:
1731 cr.set_source_rgba(*self.series_colors[number][:4])
1732
1733 self.draw_piece(angle, next_angle)
1734 cr.fill()
1735
1736 cr.set_source_rgba(1.0, 1.0, 1.0)
1737 self.draw_piece(angle, next_angle)
1738 cr.stroke()
1739
1740 angle = next_angle
1741
1742class DonutPlot(PiePlot):
1743 def __init__ (self,
1744 surface = None,
1745 data = None,
1746 width = 640,
1747 height = 480,
1748 background = "white light_gray",
1749 gradient = False,
1750 shadow = False,
1751 colors = None,
1752 inner_radius=-1):
1753
1754 Plot.__init__( self, surface, data, width, height, background, series_colors = colors )
1755
1756 self.center = ( self.dimensions[HORZ]/2, self.dimensions[VERT]/2 )
1757 self.total = sum( self.series.to_list() )
1758 self.radius = min( self.dimensions[HORZ]/3,self.dimensions[VERT]/3 )
1759 self.inner_radius = inner_radius*self.radius
1760
1761 if inner_radius == -1:
1762 self.inner_radius = self.radius/3
1763
1764 self.gradient = gradient
1765 self.shadow = shadow
1766
1767 def draw_piece(self, angle, next_angle):
1768 self.context.move_to(self.center[0] + (self.inner_radius)*math.cos(angle), self.center[1] + (self.inner_radius)*math.sin(angle))
1769 self.context.line_to(self.center[0] + self.radius*math.cos(angle), self.center[1] + self.radius*math.sin(angle))
1770 self.context.arc(self.center[0], self.center[1], self.radius, angle, next_angle)
1771 self.context.line_to(self.center[0] + (self.inner_radius)*math.cos(next_angle), self.center[1] + (self.inner_radius)*math.sin(next_angle))
1772 self.context.arc_negative(self.center[0], self.center[1], self.inner_radius, next_angle, angle)
1773 self.context.close_path()
1774
1775 def render_shadow(self):
1776 horizontal_shift = 3
1777 vertical_shift = 3
1778 self.context.set_source_rgba(0, 0, 0, 0.5)
1779 self.context.arc(self.center[0] + horizontal_shift, self.center[1] + vertical_shift, self.inner_radius, 0, 2*math.pi)
1780 self.context.arc_negative(self.center[0] + horizontal_shift, self.center[1] + vertical_shift, self.radius, 0, -2*math.pi)
1781 self.context.fill()
1782
1783class GanttChart (Plot) :
1784 def __init__(self,
1785 surface = None,
1786 data = None,
1787 width = 640,
1788 height = 480,
1789 x_labels = None,
1790 y_labels = None,
1791 colors = None):
1792 self.bounds = {}
1793 self.max_value = {}
1794 Plot.__init__(self, surface, data, width, height, x_labels = x_labels, y_labels = y_labels, series_colors = colors)
1795
1796 def load_series(self, data, x_labels=None, y_labels=None, series_colors=None):
1797 Plot.load_series(self, data, x_labels, y_labels, series_colors)
1798 self.calc_boundaries()
1799
1800 def calc_boundaries(self):
1801 self.bounds[HORZ] = (0,len(self.series))
1802 end_pos = max(self.series.to_list())
1803
1804 #for group in self.series:
1805 # if hasattr(item, "__delitem__"):
1806 # for sub_item in item:
1807 # end_pos = max(sub_item)
1808 # else:
1809 # end_pos = max(item)
1810 self.bounds[VERT] = (0,end_pos)
1811
1812 def calc_extents(self, direction):
1813 self.max_value[direction] = 0
1814 if self.labels[direction]:
1815 self.max_value[direction] = max(self.context.text_extents(item)[2] for item in self.labels[direction])
1816 else:
1817 self.max_value[direction] = self.context.text_extents( str(self.bounds[direction][1] + 1) )[2]
1818
1819 def calc_horz_extents(self):
1820 self.calc_extents(HORZ)
1821 self.borders[HORZ] = 100 + self.max_value[HORZ]
1822
1823 def calc_vert_extents(self):
1824 self.calc_extents(VERT)
1825 self.borders[VERT] = self.dimensions[VERT]/(self.bounds[HORZ][1] + 1)
1826
1827 def calc_steps(self):
1828 self.horizontal_step = (self.dimensions[HORZ] - self.borders[HORZ])/(len(self.labels[VERT]))
1829 self.vertical_step = self.borders[VERT]
1830
1831 def render(self):
1832 self.calc_horz_extents()
1833 self.calc_vert_extents()
1834 self.calc_steps()
1835 self.render_background()
1836
1837 self.render_labels()
1838 self.render_grid()
1839 self.render_plot()
1840
1841 def render_background(self):
1842 cr = self.context
1843 cr.set_source_rgba(255,255,255)
1844 cr.rectangle(0,0,self.dimensions[HORZ], self.dimensions[VERT])
1845 cr.fill()
1846 for number,group in enumerate(self.series):
1847 linear = cairo.LinearGradient(self.dimensions[HORZ]/2, self.borders[VERT] + number*self.vertical_step,
1848 self.dimensions[HORZ]/2, self.borders[VERT] + (number+1)*self.vertical_step)
1849 linear.add_color_stop_rgba(0,1.0,1.0,1.0,1.0)
1850 linear.add_color_stop_rgba(1.0,0.9,0.9,0.9,1.0)
1851 cr.set_source(linear)
1852 cr.rectangle(0,self.borders[VERT] + number*self.vertical_step,self.dimensions[HORZ],self.vertical_step)
1853 cr.fill()
1854
1855 def render_grid(self):
1856 cr = self.context
1857 cr.set_source_rgba(0.7, 0.7, 0.7)
1858 cr.set_dash((1,0,0,0,0,0,1))
1859 cr.set_line_width(0.5)
1860 for number,label in enumerate(self.labels[VERT]):
1861 h = cr.text_extents(label)[3]
1862 cr.move_to(self.borders[HORZ] + number*self.horizontal_step, self.vertical_step/2 + h)
1863 cr.line_to(self.borders[HORZ] + number*self.horizontal_step, self.dimensions[VERT])
1864 cr.stroke()
1865
1866 def render_labels(self):
1867 self.context.set_font_size(0.02 * self.dimensions[HORZ])
1868
1869 self.render_horz_labels()
1870 self.render_vert_labels()
1871
1872 def render_horz_labels(self):
1873 cr = self.context
1874 labels = self.labels[HORZ]
1875 if not labels:
1876 labels = [str(i) for i in range(1, self.bounds[HORZ][1] + 1) ]
1877 for number,label in enumerate(labels):
1878 if label != None:
1879 cr.set_source_rgba(0.5, 0.5, 0.5)
1880 w,h = cr.text_extents(label)[2], cr.text_extents(label)[3]
1881 cr.move_to(40,self.borders[VERT] + number*self.vertical_step + self.vertical_step/2 + h/2)
1882 cr.show_text(label)
1883
1884 def render_vert_labels(self):
1885 cr = self.context
1886 labels = self.labels[VERT]
1887 if not labels:
1888 labels = [str(i) for i in range(1, self.bounds[VERT][1] + 1) ]
1889 for number,label in enumerate(labels):
1890 w,h = cr.text_extents(label)[2], cr.text_extents(label)[3]
1891 cr.move_to(self.borders[HORZ] + number*self.horizontal_step - w/2, self.vertical_step/2)
1892 cr.show_text(label)
1893
1894 def render_rectangle(self, x0, y0, x1, y1, color):
1895 self.draw_shadow(x0, y0, x1, y1)
1896 self.draw_rectangle(x0, y0, x1, y1, color)
1897
1898 def draw_rectangular_shadow(self, gradient, x0, y0, w, h):
1899 self.context.set_source(gradient)
1900 self.context.rectangle(x0,y0,w,h)
1901 self.context.fill()
1902
1903 def draw_circular_shadow(self, x, y, radius, ang_start, ang_end, mult, shadow):
1904 gradient = cairo.RadialGradient(x, y, 0, x, y, 2*radius)
1905 gradient.add_color_stop_rgba(0, 0, 0, 0, shadow)
1906 gradient.add_color_stop_rgba(1, 0, 0, 0, 0)
1907 self.context.set_source(gradient)
1908 self.context.move_to(x,y)
1909 self.context.line_to(x + mult[0]*radius,y + mult[1]*radius)
1910 self.context.arc(x, y, 8, ang_start, ang_end)
1911 self.context.line_to(x,y)
1912 self.context.close_path()
1913 self.context.fill()
1914
1915 def draw_rectangle(self, x0, y0, x1, y1, color):
1916 cr = self.context
1917 middle = (x0+x1)/2
1918 linear = cairo.LinearGradient(middle,y0,middle,y1)
1919 linear.add_color_stop_rgba(0,3.5*color[0]/5.0, 3.5*color[1]/5.0, 3.5*color[2]/5.0,1.0)
1920 linear.add_color_stop_rgba(1,*color[:4])
1921 cr.set_source(linear)
1922
1923 cr.arc(x0+5, y0+5, 5, 0, 2*math.pi)
1924 cr.arc(x1-5, y0+5, 5, 0, 2*math.pi)
1925 cr.arc(x0+5, y1-5, 5, 0, 2*math.pi)
1926 cr.arc(x1-5, y1-5, 5, 0, 2*math.pi)
1927 cr.rectangle(x0+5,y0,x1-x0-10,y1-y0)
1928 cr.rectangle(x0,y0+5,x1-x0,y1-y0-10)
1929 cr.fill()
1930
1931 def draw_shadow(self, x0, y0, x1, y1):
1932 shadow = 0.4
1933 h_mid = (x0+x1)/2
1934 v_mid = (y0+y1)/2
1935 h_linear_1 = cairo.LinearGradient(h_mid,y0-4,h_mid,y0+4)
1936 h_linear_2 = cairo.LinearGradient(h_mid,y1-4,h_mid,y1+4)
1937 v_linear_1 = cairo.LinearGradient(x0-4,v_mid,x0+4,v_mid)
1938 v_linear_2 = cairo.LinearGradient(x1-4,v_mid,x1+4,v_mid)
1939
1940 h_linear_1.add_color_stop_rgba( 0, 0, 0, 0, 0)
1941 h_linear_1.add_color_stop_rgba( 1, 0, 0, 0, shadow)
1942 h_linear_2.add_color_stop_rgba( 0, 0, 0, 0, shadow)
1943 h_linear_2.add_color_stop_rgba( 1, 0, 0, 0, 0)
1944 v_linear_1.add_color_stop_rgba( 0, 0, 0, 0, 0)
1945 v_linear_1.add_color_stop_rgba( 1, 0, 0, 0, shadow)
1946 v_linear_2.add_color_stop_rgba( 0, 0, 0, 0, shadow)
1947 v_linear_2.add_color_stop_rgba( 1, 0, 0, 0, 0)
1948
1949 self.draw_rectangular_shadow(h_linear_1,x0+4,y0-4,x1-x0-8,8)
1950 self.draw_rectangular_shadow(h_linear_2,x0+4,y1-4,x1-x0-8,8)
1951 self.draw_rectangular_shadow(v_linear_1,x0-4,y0+4,8,y1-y0-8)
1952 self.draw_rectangular_shadow(v_linear_2,x1-4,y0+4,8,y1-y0-8)
1953
1954 self.draw_circular_shadow(x0+4, y0+4, 4, math.pi, 3*math.pi/2, (-1,0), shadow)
1955 self.draw_circular_shadow(x1-4, y0+4, 4, 3*math.pi/2, 2*math.pi, (0,-1), shadow)
1956 self.draw_circular_shadow(x0+4, y1-4, 4, math.pi/2, math.pi, (0,1), shadow)
1957 self.draw_circular_shadow(x1-4, y1-4, 4, 0, math.pi/2, (1,0), shadow)
1958
1959 def render_plot(self):
1960 for index,group in enumerate(self.series):
1961 for data in group:
1962 self.render_rectangle(self.borders[HORZ] + data.content[0]*self.horizontal_step,
1963 self.borders[VERT] + index*self.vertical_step + self.vertical_step/4.0,
1964 self.borders[HORZ] + data.content[1]*self.horizontal_step,
1965 self.borders[VERT] + index*self.vertical_step + 3.0*self.vertical_step/4.0,
1966 self.series_colors[index])
1967
1968# Function definition
1969
1970def scatter_plot(name,
1971 data = None,
1972 errorx = None,
1973 errory = None,
1974 width = 640,
1975 height = 480,
1976 background = "white light_gray",
1977 border = 0,
1978 axis = False,
1979 dash = False,
1980 discrete = False,
1981 dots = False,
1982 grid = False,
1983 series_legend = False,
1984 x_labels = None,
1985 y_labels = None,
1986 x_bounds = None,
1987 y_bounds = None,
1988 z_bounds = None,
1989 x_title = None,
1990 y_title = None,
1991 series_colors = None,
1992 circle_colors = None):
1993
1994 '''
1995 - Function to plot scatter data.
1996
1997 - Parameters
1998
1999 data - The values to be ploted might be passed in a two basic:
2000 list of points: [(0,0), (0,1), (0,2)] or [(0,0,1), (0,1,4), (0,2,1)]
2001 lists of coordinates: [ [0,0,0] , [0,1,2] ] or [ [0,0,0] , [0,1,2] , [1,4,1] ]
2002 Notice that these kinds of that can be grouped in order to form more complex data
2003 using lists of lists or dictionaries;
2004 series_colors - Define color values for each of the series
2005 circle_colors - Define a lower and an upper bound for the circle colors for variable radius
2006 (3 dimensions) series
2007 '''
2008
2009 plot = ScatterPlot( name, data, errorx, errory, width, height, background, border,
2010 axis, dash, discrete, dots, grid, series_legend, x_labels, y_labels,
2011 x_bounds, y_bounds, z_bounds, x_title, y_title, series_colors, circle_colors )
2012 plot.render()
2013 plot.commit()
2014
2015def dot_line_plot(name,
2016 data,
2017 width,
2018 height,
2019 background = "white light_gray",
2020 border = 0,
2021 axis = False,
2022 dash = False,
2023 dots = False,
2024 grid = False,
2025 series_legend = False,
2026 x_labels = None,
2027 y_labels = None,
2028 x_bounds = None,
2029 y_bounds = None,
2030 x_title = None,
2031 y_title = None,
2032 series_colors = None):
2033 '''
2034 - Function to plot graphics using dots and lines.
2035
2036 dot_line_plot (name, data, width, height, background = "white light_gray", border = 0, axis = False, grid = False, x_labels = None, y_labels = None, x_bounds = None, y_bounds = None)
2037
2038 - Parameters
2039
2040 name - Name of the desired output file, no need to input the .svg as it will be added at runtim;
2041 data - The list, list of lists or dictionary holding the data to be plotted;
2042 width, height - Dimensions of the output image;
2043 background - A 3 element tuple representing the rgb color expected for the background or a new cairo linear gradient.
2044 If left None, a gray to white gradient will be generated;
2045 border - Distance in pixels of a square border into which the graphics will be drawn;
2046 axis - Whether or not the axis are to be drawn;
2047 dash - Boolean or a list or a dictionary of booleans indicating whether or not the associated series should be drawn in dashed mode;
2048 dots - Whether or not dots should be drawn on each point;
2049 grid - Whether or not the gris is to be drawn;
2050 series_legend - Whether or not the legend is to be drawn;
2051 x_labels, y_labels - lists of strings containing the horizontal and vertical labels for the axis;
2052 x_bounds, y_bounds - tuples containing the lower and upper value bounds for the data to be plotted;
2053 x_title - Whether or not to plot a title over the x axis.
2054 y_title - Whether or not to plot a title over the y axis.
2055
2056 - Examples of use
2057
2058 data = [0, 1, 3, 8, 9, 0, 10, 10, 2, 1]
2059 CairoPlot.dot_line_plot('teste', data, 400, 300)
2060
2061 data = { "john" : [10, 10, 10, 10, 30], "mary" : [0, 0, 3, 5, 15], "philip" : [13, 32, 11, 25, 2] }
2062 x_labels = ["jan/2008", "feb/2008", "mar/2008", "apr/2008", "may/2008" ]
2063 CairoPlot.dot_line_plot( 'test', data, 400, 300, axis = True, grid = True,
2064 series_legend = True, x_labels = x_labels )
2065 '''
2066 plot = DotLinePlot( name, data, width, height, background, border,
2067 axis, dash, dots, grid, series_legend, x_labels, y_labels,
2068 x_bounds, y_bounds, x_title, y_title, series_colors )
2069 plot.render()
2070 plot.commit()
2071
2072def function_plot(name,
2073 data,
2074 width,
2075 height,
2076 background = "white light_gray",
2077 border = 0,
2078 axis = True,
2079 dots = False,
2080 discrete = False,
2081 grid = False,
2082 series_legend = False,
2083 x_labels = None,
2084 y_labels = None,
2085 x_bounds = None,
2086 y_bounds = None,
2087 x_title = None,
2088 y_title = None,
2089 series_colors = None,
2090 step = 1):
2091
2092 '''
2093 - Function to plot functions.
2094
2095 function_plot(name, data, width, height, background = "white light_gray", border = 0, axis = True, grid = False, dots = False, x_labels = None, y_labels = None, x_bounds = None, y_bounds = None, step = 1, discrete = False)
2096
2097 - Parameters
2098
2099 name - Name of the desired output file, no need to input the .svg as it will be added at runtim;
2100 data - The list, list of lists or dictionary holding the data to be plotted;
2101 width, height - Dimensions of the output image;
2102 background - A 3 element tuple representing the rgb color expected for the background or a new cairo linear gradient.
2103 If left None, a gray to white gradient will be generated;
2104 border - Distance in pixels of a square border into which the graphics will be drawn;
2105 axis - Whether or not the axis are to be drawn;
2106 grid - Whether or not the gris is to be drawn;
2107 dots - Whether or not dots should be shown at each point;
2108 x_labels, y_labels - lists of strings containing the horizontal and vertical labels for the axis;
2109 x_bounds, y_bounds - tuples containing the lower and upper value bounds for the data to be plotted;
2110 step - the horizontal distance from one point to the other. The smaller, the smoother the curve will be;
2111 discrete - whether or not the function should be plotted in discrete format.
2112
2113 - Example of use
2114
2115 data = lambda x : x**2
2116 CairoPlot.function_plot('function4', data, 400, 300, grid = True, x_bounds=(-10,10), step = 0.1)
2117 '''
2118
2119 plot = FunctionPlot( name, data, width, height, background, border,
2120 axis, discrete, dots, grid, series_legend, x_labels, y_labels,
2121 x_bounds, y_bounds, x_title, y_title, series_colors, step )
2122 plot.render()
2123 plot.commit()
2124
2125def pie_plot( name, data, width, height, background = "white light_gray", gradient = False, shadow = False, colors = None ):
2126
2127 '''
2128 - Function to plot pie graphics.
2129
2130 pie_plot(name, data, width, height, background = "white light_gray", gradient = False, colors = None)
2131
2132 - Parameters
2133
2134 name - Name of the desired output file, no need to input the .svg as it will be added at runtim;
2135 data - The list, list of lists or dictionary holding the data to be plotted;
2136 width, height - Dimensions of the output image;
2137 background - A 3 element tuple representing the rgb color expected for the background or a new cairo linear gradient.
2138 If left None, a gray to white gradient will be generated;
2139 gradient - Whether or not the pie color will be painted with a gradient;
2140 shadow - Whether or not there will be a shadow behind the pie;
2141 colors - List of slices colors.
2142
2143 - Example of use
2144
2145 teste_data = {"john" : 123, "mary" : 489, "philip" : 890 , "suzy" : 235}
2146 CairoPlot.pie_plot("pie_teste", teste_data, 500, 500)
2147 '''
2148
2149 plot = PiePlot( name, data, width, height, background, gradient, shadow, colors )
2150 plot.render()
2151 plot.commit()
2152
2153def donut_plot(name, data, width, height, background = "white light_gray", gradient = False, shadow = False, colors = None, inner_radius = -1):
2154
2155 '''
2156 - Function to plot donut graphics.
2157
2158 donut_plot(name, data, width, height, background = "white light_gray", gradient = False, inner_radius = -1)
2159
2160 - Parameters
2161
2162 name - Name of the desired output file, no need to input the .svg as it will be added at runtim;
2163 data - The list, list of lists or dictionary holding the data to be plotted;
2164 width, height - Dimensions of the output image;
2165 background - A 3 element tuple representing the rgb color expected for the background or a new cairo linear gradient.
2166 If left None, a gray to white gradient will be generated;
2167 shadow - Whether or not there will be a shadow behind the donut;
2168 gradient - Whether or not the donut color will be painted with a gradient;
2169 colors - List of slices colors;
2170 inner_radius - The radius of the donut's inner circle.
2171
2172 - Example of use
2173
2174 teste_data = {"john" : 123, "mary" : 489, "philip" : 890 , "suzy" : 235}
2175 CairoPlot.donut_plot("donut_teste", teste_data, 500, 500)
2176 '''
2177
2178 plot = DonutPlot(name, data, width, height, background, gradient, shadow, colors, inner_radius)
2179 plot.render()
2180 plot.commit()
2181
2182def gantt_chart(name, pieces, width, height, x_labels, y_labels, colors):
2183
2184 '''
2185 - Function to generate Gantt Charts.
2186
2187 gantt_chart(name, pieces, width, height, x_labels, y_labels, colors):
2188
2189 - Parameters
2190
2191 name - Name of the desired output file, no need to input the .svg as it will be added at runtim;
2192 pieces - A list defining the spaces to be drawn. The user must pass, for each line, the index of its start and the index of its end. If a line must have two or more spaces, they must be passed inside a list;
2193 width, height - Dimensions of the output image;
2194 x_labels - A list of names for each of the vertical lines;
2195 y_labels - A list of names for each of the horizontal spaces;
2196 colors - List containing the colors expected for each of the horizontal spaces
2197
2198 - Example of use
2199
2200 pieces = [ (0.5,5.5) , [(0,4),(6,8)] , (5.5,7) , (7,8)]
2201 x_labels = [ 'teste01', 'teste02', 'teste03', 'teste04']
2202 y_labels = [ '0001', '0002', '0003', '0004', '0005', '0006', '0007', '0008', '0009', '0010' ]
2203 colors = [ (1.0, 0.0, 0.0), (1.0, 0.7, 0.0), (1.0, 1.0, 0.0), (0.0, 1.0, 0.0) ]
2204 CairoPlot.gantt_chart('gantt_teste', pieces, 600, 300, x_labels, y_labels, colors)
2205 '''
2206
2207 plot = GanttChart(name, pieces, width, height, x_labels, y_labels, colors)
2208 plot.render()
2209 plot.commit()
2210
2211def vertical_bar_plot(name,
2212 data,
2213 width,
2214 height,
2215 background = "white light_gray",
2216 border = 0,
2217 display_values = False,
2218 grid = False,
2219 rounded_corners = False,
2220 stack = False,
2221 three_dimension = False,
2222 series_labels = None,
2223 x_labels = None,
2224 y_labels = None,
2225 x_bounds = None,
2226 y_bounds = None,
2227 colors = None):
2228 #TODO: Fix docstring for vertical_bar_plot
2229 '''
2230 - Function to generate vertical Bar Plot Charts.
2231
2232 bar_plot(name, data, width, height, background, border, grid, rounded_corners, three_dimension,
2233 x_labels, y_labels, x_bounds, y_bounds, colors):
2234
2235 - Parameters
2236
2237 name - Name of the desired output file, no need to input the .svg as it will be added at runtime;
2238 data - The list, list of lists or dictionary holding the data to be plotted;
2239 width, height - Dimensions of the output image;
2240 background - A 3 element tuple representing the rgb color expected for the background or a new cairo linear gradient.
2241 If left None, a gray to white gradient will be generated;
2242 border - Distance in pixels of a square border into which the graphics will be drawn;
2243 grid - Whether or not the gris is to be drawn;
2244 rounded_corners - Whether or not the bars should have rounded corners;
2245 three_dimension - Whether or not the bars should be drawn in pseudo 3D;
2246 x_labels, y_labels - lists of strings containing the horizontal and vertical labels for the axis;
2247 x_bounds, y_bounds - tuples containing the lower and upper value bounds for the data to be plotted;
2248 colors - List containing the colors expected for each of the bars.
2249
2250 - Example of use
2251
2252 data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2253 CairoPlot.vertical_bar_plot ('bar2', data, 400, 300, border = 20, grid = True, rounded_corners = False)
2254 '''
2255
2256 plot = VerticalBarPlot(name, data, width, height, background, border,
2257 display_values, grid, rounded_corners, stack, three_dimension,
2258 series_labels, x_labels, y_labels, x_bounds, y_bounds, colors)
2259 plot.render()
2260 plot.commit()
2261
2262def horizontal_bar_plot(name,
2263 data,
2264 width,
2265 height,
2266 background = "white light_gray",
2267 border = 0,
2268 display_values = False,
2269 grid = False,
2270 rounded_corners = False,
2271 stack = False,
2272 three_dimension = False,
2273 series_labels = None,
2274 x_labels = None,
2275 y_labels = None,
2276 x_bounds = None,
2277 y_bounds = None,
2278 colors = None):
2279
2280 #TODO: Fix docstring for horizontal_bar_plot
2281 '''
2282 - Function to generate Horizontal Bar Plot Charts.
2283
2284 bar_plot(name, data, width, height, background, border, grid, rounded_corners, three_dimension,
2285 x_labels, y_labels, x_bounds, y_bounds, colors):
2286
2287 - Parameters
2288
2289 name - Name of the desired output file, no need to input the .svg as it will be added at runtime;
2290 data - The list, list of lists or dictionary holding the data to be plotted;
2291 width, height - Dimensions of the output image;
2292 background - A 3 element tuple representing the rgb color expected for the background or a new cairo linear gradient.
2293 If left None, a gray to white gradient will be generated;
2294 border - Distance in pixels of a square border into which the graphics will be drawn;
2295 grid - Whether or not the gris is to be drawn;
2296 rounded_corners - Whether or not the bars should have rounded corners;
2297 three_dimension - Whether or not the bars should be drawn in pseudo 3D;
2298 x_labels, y_labels - lists of strings containing the horizontal and vertical labels for the axis;
2299 x_bounds, y_bounds - tuples containing the lower and upper value bounds for the data to be plotted;
2300 colors - List containing the colors expected for each of the bars.
2301
2302 - Example of use
2303
2304 data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2305 CairoPlot.bar_plot ('bar2', data, 400, 300, border = 20, grid = True, rounded_corners = False)
2306 '''
2307
2308 plot = HorizontalBarPlot(name, data, width, height, background, border,
2309 display_values, grid, rounded_corners, stack, three_dimension,
2310 series_labels, x_labels, y_labels, x_bounds, y_bounds, colors)
2311 plot.render()
2312 plot.commit()
2313
2314def stream_chart(name,
2315 data,
2316 width,
2317 height,
2318 background = "white light_gray",
2319 border = 0,
2320 grid = False,
2321 series_legend = None,
2322 x_labels = None,
2323 x_bounds = None,
2324 y_bounds = None,
2325 colors = None):
2326
2327 #TODO: Fix docstring for horizontal_bar_plot
2328 plot = StreamChart(name, data, width, height, background, border,
2329 grid, series_legend, x_labels, x_bounds, y_bounds, colors)
2330 plot.render()
2331 plot.commit()
2332
2333
2334if __name__ == "__main__":
2335 import tests
2336 import seriestests
This page took 0.110025 seconds and 4 git commands to generate.