Merge lp:~mrooney/cairoplot/label-formatters into lp:cairoplot

Proposed by Michael Rooney
Status: Needs review
Proposed branch: lp:~mrooney/cairoplot/label-formatters
Merge into: lp:cairoplot
Diff against target: 303 lines (+94/-34)
4 files modified
trunk/cairoplot.py (+48/-27)
trunk/seriestests.py (+21/-7)
trunk/testscripts/compare.sh (+22/-0)
trunk/testscripts/timeit.sh (+3/-0)
To merge this branch: bzr merge lp:~mrooney/cairoplot/label-formatters
Reviewer Review Type Date Requested Status
Rodrigo Moreira Araújo Pending
Review via email: mp+30192@code.launchpad.net

Description of the change

Add value/label formatters for displaying axis/values as any kind of string such as date, currency, et cetera.

To post a comment you must log in.
Revision history for this message
Rodrigo Moreira Araújo (alf-rodrigo) wrote :
Download full text (13.9 KiB)

Hello there,

As I wasn't too busy, i just reviewed the code. The only thing I didn't get
was the use of this compare.sh script. Why would you like to compare png
files?

Great code btw, merging it soon.

Cheers,

Rodrigo Araujo

2010/7/17 Michael Rooney <email address hidden>

> Michael Rooney has proposed merging lp:~mrooney/cairoplot/label-formatters
> into lp:cairoplot.
>
> Requested reviews:
> Rodrigo Moreira Araújo (alf-rodrigo)
>
>
> Add value/label formatters for displaying axis/values as any kind of string
> such as date, currency, et cetera.
> --
> https://code.launchpad.net/~mrooney/cairoplot/label-formatters/+merge/30192
> You are requested to review the proposed merge of
> lp:~mrooney/cairoplot/label-formatters into lp:cairoplot.
>
> === modified file 'trunk/cairoplot.py'
> --- trunk/cairoplot.py 2009-07-09 21:57:24 +0000
> +++ trunk/cairoplot.py 2010-07-17 21:38:38 +0000
> @@ -1,4 +1,4 @@
> -#!/usr/bin/env python
> +#!/usr/bin/env python
> # -*- coding: utf-8 -*-
>
> # CairoPlot.py
> @@ -288,6 +288,8 @@
> series_legend = False,
> x_labels = None,
> y_labels = None,
> + x_formatter = None,
> + y_formatter = None,
> x_bounds = None,
> y_bounds = None,
> z_bounds = None,
> @@ -303,6 +305,9 @@
> self.titles = {}
> self.titles[HORZ] = x_title
> self.titles[VERT] = y_title
> + self.label_formatters = {}
> + self.label_formatters[HORZ] = x_formatter
> + self.label_formatters[VERT] = y_formatter
> self.max_value = {}
> self.axis = axis
> self.discrete = discrete
> @@ -396,19 +401,17 @@
> self.errors[VERT] = [errory]
>
> def calc_labels(self):
> - if not self.labels[HORZ]:
> - amplitude = self.bounds[HORZ][1] - self.bounds[HORZ][0]
> - if amplitude % 10: #if horizontal labels need floating points
> - self.labels[HORZ] = ["%.2lf" % (float(self.bounds[HORZ][0]
> + (amplitude * i / 10.0))) for i in range(11) ]
> - else:
> - self.labels[HORZ] = ["%d" % (int(self.bounds[HORZ][0] +
> (amplitude * i / 10.0))) for i in range(11) ]
> - if not self.labels[VERT]:
> - amplitude = self.bounds[VERT][1] - self.bounds[VERT][0]
> - if amplitude % 10: #if vertical labels need floating points
> - self.labels[VERT] = ["%.2lf" % (float(self.bounds[VERT][0]
> + (amplitude * i / 10.0))) for i in range(11) ]
> - else:
> - self.labels[VERT] = ["%d" % (int(self.bounds[VERT][0] +
> (amplitude * i / 10.0))) for i in range(11) ]
> -
> + for key in (HORZ, VERT):
> + if not self.labels[key]:
> + amplitude = self.bounds[key][1] - self.bounds[key][0]
> + labels = (self.bounds[key][0] + (amplitude * i / 10.0) for
> i in range(11))
> + if self.label_formatters[key]:
> + self.labels[key] = [self.label_formatters[key](label)
> for label in labels]
> + elif amplitude % 10: #if horizontal labels need floating
> points
> + ...

Revision history for this message
Michael Rooney (mrooney) wrote :

> Hello there,
>
> As I wasn't too busy, i just reviewed the code. The only thing I didn't get
> was the use of this compare.sh script. Why would you like to compare png
> files?
>
> Great code btw, merging it soon.
>

Thanks! That is showing up because it is including the changes from the speedup branch made by Karel, since this branch is a branch of that branch. I believe it was to make sure the generated images after the speedup changes are identical in testing, to what they should be or were before. It may not need to be in version control once you approve the merge, like the timeit.sh script which is just meant to demonstrate the improvements I believe.

If you like and are going to merge that branch anyway, you can merge that one first and I can re-propose a merge based off of that which should be simpler as it would only be r47-49.

Let me know whatever you prefer.

50. By Michael Rooney

make sure value is defined for stacked vertical bar plots

Revision history for this message
Michael Rooney (mrooney) wrote :

Just a note, I committed r50 to fix stacked vertical plots which I likely broke.

Unmerged revisions

50. By Michael Rooney

make sure value is defined for stacked vertical bar plots

49. By Michael Rooney

fix centering of formatted values in a vertical bar plot

48. By Michael Rooney

add support for value formatters to a vertical bar plot

47. By Michael Rooney

add support for x/y label formatters for a scatter plot

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'trunk/cairoplot.py'
2--- trunk/cairoplot.py 2009-07-09 21:57:24 +0000
3+++ trunk/cairoplot.py 2010-07-17 22:44:41 +0000
4@@ -1,4 +1,4 @@
5-#!/usr/bin/env python
6+#!/usr/bin/env python
7 # -*- coding: utf-8 -*-
8
9 # CairoPlot.py
10@@ -288,6 +288,8 @@
11 series_legend = False,
12 x_labels = None,
13 y_labels = None,
14+ x_formatter = None,
15+ y_formatter = None,
16 x_bounds = None,
17 y_bounds = None,
18 z_bounds = None,
19@@ -303,6 +305,9 @@
20 self.titles = {}
21 self.titles[HORZ] = x_title
22 self.titles[VERT] = y_title
23+ self.label_formatters = {}
24+ self.label_formatters[HORZ] = x_formatter
25+ self.label_formatters[VERT] = y_formatter
26 self.max_value = {}
27 self.axis = axis
28 self.discrete = discrete
29@@ -396,19 +401,17 @@
30 self.errors[VERT] = [errory]
31
32 def calc_labels(self):
33- if not self.labels[HORZ]:
34- amplitude = self.bounds[HORZ][1] - self.bounds[HORZ][0]
35- if amplitude % 10: #if horizontal labels need floating points
36- self.labels[HORZ] = ["%.2lf" % (float(self.bounds[HORZ][0] + (amplitude * i / 10.0))) for i in range(11) ]
37- else:
38- self.labels[HORZ] = ["%d" % (int(self.bounds[HORZ][0] + (amplitude * i / 10.0))) for i in range(11) ]
39- if not self.labels[VERT]:
40- amplitude = self.bounds[VERT][1] - self.bounds[VERT][0]
41- if amplitude % 10: #if vertical labels need floating points
42- self.labels[VERT] = ["%.2lf" % (float(self.bounds[VERT][0] + (amplitude * i / 10.0))) for i in range(11) ]
43- else:
44- self.labels[VERT] = ["%d" % (int(self.bounds[VERT][0] + (amplitude * i / 10.0))) for i in range(11) ]
45-
46+ for key in (HORZ, VERT):
47+ if not self.labels[key]:
48+ amplitude = self.bounds[key][1] - self.bounds[key][0]
49+ labels = (self.bounds[key][0] + (amplitude * i / 10.0) for i in range(11))
50+ if self.label_formatters[key]:
51+ self.labels[key] = [self.label_formatters[key](label) for label in labels]
52+ elif amplitude % 10: #if horizontal labels need floating points
53+ self.labels[key] = ["%.2lf" % float(label) for label in labels]
54+ else:
55+ self.labels[key] = ["%d" % int(label) for label in labels]
56+
57 def calc_extents(self, direction):
58 self.context.set_font_size(self.font_size * 0.8)
59 self.max_value[direction] = max(self.context.text_extents(item)[2] for item in self.labels[direction])
60@@ -510,9 +513,10 @@
61 if self.titles[VERT]:
62 title_width,title_height = cr.text_extents(self.titles[VERT])[2:4]
63 cr.move_to( self.dimensions[HORZ] - self.borders[HORZ] + title_height/2, self.dimensions[VERT]/2 - title_width/2)
64+ cr.save()
65 cr.rotate( math.pi/2 )
66 cr.show_text( self.titles[VERT] )
67- cr.rotate( -math.pi/2 )
68+ cr.restore()
69
70 def render_grid(self):
71 cr = self.context
72@@ -544,21 +548,29 @@
73 cr = self.context
74 step = float( self.plot_width ) / ( len( self.labels[HORZ] ) - 1 )
75 x = self.borders[HORZ]
76+ y = self.dimensions[VERT] - self.borders[VERT] + 5
77+
78+ # store rotation matrix from the initial state
79+ rotation_matrix = cr.get_matrix()
80+ rotation_matrix.rotate(self.x_label_angle)
81+
82+ cr.set_source_rgba(*self.label_color)
83+
84 for item in self.labels[HORZ]:
85- cr.set_source_rgba(*self.label_color)
86 width = cr.text_extents(item)[2]
87- cr.move_to(x, self.dimensions[VERT] - self.borders[VERT] + 5)
88- cr.rotate(self.x_label_angle)
89+ cr.move_to(x, y)
90+ cr.save()
91+ cr.set_matrix(rotation_matrix)
92 cr.show_text(item)
93- cr.rotate(-self.x_label_angle)
94+ cr.restore()
95 x += step
96
97 def render_vert_labels(self):
98 cr = self.context
99 step = ( self.plot_height ) / ( len( self.labels[VERT] ) - 1 )
100 y = self.plot_top
101+ cr.set_source_rgba(*self.label_color)
102 for item in self.labels[VERT]:
103- cr.set_source_rgba(*self.label_color)
104 width = cr.text_extents(item)[2]
105 cr.move_to(self.borders[HORZ] - width - 5,y)
106 cr.show_text(item)
107@@ -1283,12 +1295,14 @@
108 y_labels = None,
109 x_bounds = None,
110 y_bounds = None,
111- series_colors = None):
112+ series_colors = None,
113+ value_formatter = None):
114
115 BarPlot.__init__(self, surface, data, width, height, background, border,
116 display_values, grid, rounded_corners, stack, three_dimension,
117 x_labels, y_labels, x_bounds, y_bounds, series_colors, VERT)
118 self.series_labels = series_labels
119+ self.value_formatter = value_formatter or str
120
121 def calc_vert_extents(self):
122 self.calc_extents(VERT)
123@@ -1386,19 +1400,21 @@
124 if self.stack:
125 for i,group in enumerate(self.series):
126 value = sum(group.to_list())
127- width = self.context.text_extents(str(value))[2]
128+ strvalue = self.value_formatter(value)
129+ width = self.context.text_extents(strvalue)[2]
130 x = self.borders[HORZ] + (i+0.5)*self.steps[HORZ] + (i+1)*self.space - width/2
131 y = value*self.steps[VERT] + 2
132 self.context.move_to(x, self.plot_top-y)
133- self.context.show_text(str(value))
134+ self.context.show_text(strvalue)
135 else:
136 for i,group in enumerate(self.series):
137 inner_step = self.steps[HORZ]/len(group)
138 x0 = self.borders[HORZ] + i*self.steps[HORZ] + (i+1)*self.space
139 for number,data in enumerate(group):
140- width = self.context.text_extents(str(data.content))[2]
141+ strvalue = self.value_formatter(data.content)
142+ width = self.context.text_extents(strvalue)[2]
143 self.context.move_to(x0 + 0.5*inner_step - width/2, self.plot_top - data.content*self.steps[VERT] - 2)
144- self.context.show_text(str(data.content))
145+ self.context.show_text(strvalue)
146 x0 += inner_step
147
148 def render_plot(self):
149@@ -1974,6 +1990,8 @@
150 series_legend = False,
151 x_labels = None,
152 y_labels = None,
153+ x_formatter = None,
154+ y_formatter = None,
155 x_bounds = None,
156 y_bounds = None,
157 z_bounds = None,
158@@ -1999,6 +2017,7 @@
159
160 plot = ScatterPlot( name, data, errorx, errory, width, height, background, border,
161 axis, dash, discrete, dots, grid, series_legend, x_labels, y_labels,
162+ x_formatter, y_formatter,
163 x_bounds, y_bounds, z_bounds, x_title, y_title, series_colors, circle_colors )
164 plot.render()
165 plot.commit()
166@@ -2215,7 +2234,8 @@
167 y_labels = None,
168 x_bounds = None,
169 y_bounds = None,
170- colors = None):
171+ colors = None,
172+ value_formatter = None):
173 #TODO: Fix docstring for vertical_bar_plot
174 '''
175 - Function to generate vertical Bar Plot Charts.
176@@ -2237,6 +2257,7 @@
177 x_labels, y_labels - lists of strings containing the horizontal and vertical labels for the axis;
178 x_bounds, y_bounds - tuples containing the lower and upper value bounds for the data to be plotted;
179 colors - List containing the colors expected for each of the bars.
180+ value_formatter - if present, this function will be called with the value, and the string it returns will be used
181
182 - Example of use
183
184@@ -2246,7 +2267,7 @@
185
186 plot = VerticalBarPlot(name, data, width, height, background, border,
187 display_values, grid, rounded_corners, stack, three_dimension,
188- series_labels, x_labels, y_labels, x_bounds, y_bounds, colors)
189+ series_labels, x_labels, y_labels, x_bounds, y_bounds, colors, value_formatter)
190 plot.render()
191 plot.commit()
192
193
194=== modified file 'trunk/seriestests.py'
195--- trunk/seriestests.py 2009-07-09 21:57:24 +0000
196+++ trunk/seriestests.py 2010-07-17 22:44:41 +0000
197@@ -1,8 +1,16 @@
198-import cairo, math, random
199+import cairo, math, sys
200
201 import cairoplot
202 from series import Series
203
204+# non-random data for needs of visual comparison of changes
205+if '--non-random' in sys.argv:
206+ random = lambda : 1.0
207+ print 'Plotting nonrandom data'
208+else:
209+ import random
210+ random = random.random
211+
212 # Line plotting
213 test_scatter_plot = 1
214 test_dot_line_plot = 1
215@@ -48,8 +56,8 @@
216 f = [math.exp(x) for x in t]
217 g = [10*math.cos(x) for x in t]
218 h = [10*math.sin(x) for x in t]
219- erx = [0.1*random.random() for x in t]
220- ery = [5*random.random() for x in t]
221+ erx = [0.1*random() for x in t]
222+ ery = [5*random() for x in t]
223 data = Series({"exp" : [t,f], "cos" : [t,g], "sin" : [t,h]})
224 series_colors = [ (1,0,0), (0,0,0), (0,0,1) ]
225 cairoplot.scatter_plot ( 'cross_r_exponential_series.png', data = data, errorx = [erx,erx], errory = [ery,ery], width = 800, height = 600, border = 20,
226@@ -77,6 +85,12 @@
227 cairoplot.dot_line_plot( 'dot_line_3_series_legend_series.png', data, 400, 300, x_labels = x_labels,
228 axis = True, grid = True, series_legend = True )
229
230+ #Speed test, many x_labels
231+ data = range(1000)
232+ x_labels = [str(x) for x in data]
233+ cairoplot.dot_line_plot( 'dot_line_4_many_x_labels.png', data, 14000, 300, x_labels = x_labels,
234+ axis = True, grid = True, series_legend = True )
235+
236 if test_function_plot :
237 #Default Plot
238 data = lambda x : x**2
239@@ -142,7 +156,7 @@
240 cairoplot.vertical_bar_plot ( 'vbar_8_hy_labels_series.png', data, 600, 200, border = 20, display_values = True, grid = True, x_labels = x_labels, y_labels = y_labels )
241
242 #Large data set
243- data = Series([[10*random.random()] for x in range(50)])
244+ data = Series([[10*random()] for x in range(50)])
245 x_labels = ["large label name oh my god it's big" for x in data]
246 cairoplot.vertical_bar_plot ( 'vbar_9_large_series.png', data, 1000, 800, border = 20, grid = True, rounded_corners = True, x_labels = x_labels )
247
248@@ -182,7 +196,7 @@
249 cairoplot.horizontal_bar_plot ( 'hbar_8_hy_labels_series.png', data, 600, 200, border = 20, series_labels = series_labels, display_values = True, grid = True, x_labels = x_labels, y_labels = y_labels )
250
251 #Large data set
252- data = Series([[10*random.random()] for x in range(25)])
253+ data = Series([[10*random()] for x in range(25)])
254 x_labels = ["large label name oh my god it's big" for x in data]
255 cairoplot.horizontal_bar_plot ( 'hbar_9_large_series.png', data, 1000, 800, border = 20, grid = True, rounded_corners = True, x_labels = x_labels )
256
257@@ -242,8 +256,8 @@
258 f = [math.exp(x) for x in t]
259 g = [10*math.cos(x) for x in t]
260 h = [10*math.sin(x) for x in t]
261- erx = [0.1*random.random() for x in t]
262- ery = [5*random.random() for x in t]
263+ erx = [0.1*random() for x in t]
264+ ery = [5*random() for x in t]
265 data = Series({"exp" : [t,f], "cos" : [t,g], "sin" : [t,h]})
266 series_colors = [ (1,0,0), (0,0,0) ]
267 cairoplot.scatter_plot ( 'scatter_color_themes_series.png', data = data, errorx = [erx,erx], errory = [ery,ery], width = 800, height = 600, border = 20,
268
269=== added directory 'trunk/testscripts'
270=== added file 'trunk/testscripts/compare.sh'
271--- trunk/testscripts/compare.sh 1970-01-01 00:00:00 +0000
272+++ trunk/testscripts/compare.sh 2010-07-17 22:44:41 +0000
273@@ -0,0 +1,22 @@
274+#!/bin/sh
275+
276+if [ $# -ne 3 ]
277+then
278+ echo "Compare .png files in two directories"
279+ echo "Usage: ./compare.sh path1 path2 diffdir"
280+ echo "Example: ./compare.sh . ../other ./diff"
281+ exit
282+fi
283+
284+for dir in $1 $2
285+do
286+ for i in $dir/*.png
287+ do
288+ convert $i $i.tiff
289+ done
290+done
291+
292+for i in `(cd $1; ls *.tiff)`
293+do
294+ perceptualdiff $1/$i $2/$i -output $3/$i.ppm
295+done
296
297=== added file 'trunk/testscripts/timeit.sh'
298--- trunk/testscripts/timeit.sh 1970-01-01 00:00:00 +0000
299+++ trunk/testscripts/timeit.sh 2010-07-17 22:44:41 +0000
300@@ -0,0 +1,3 @@
301+#!/bin/sh
302+DIRNAME=`dirname $0`
303+(cd $DIRNAME/..; python -m timeit -n 1 -v "import seriestests")

Subscribers

People subscribed via source and target branches