View difference between Paste ID: 6b8jcanU and
SHOW:
|
|
- or go back to the newest paste.
1 | - | |
1 | + | #!/usr/bin/env python |
2 | # -*- coding: cp1252 -*- | |
3 | ||
4 | # nicechart.py | |
5 | # | |
6 | # Copyright 2011 | |
7 | # | |
8 | # Christoph Sterz | |
9 | # Florian Weber | |
10 | # | |
11 | # This program is free software; you can redistribute it and/or modify | |
12 | # it under the terms of the GNU General Public License as published by | |
13 | # the Free Software Foundation; either version 3 of the License, or | |
14 | # (at your option) any later version. | |
15 | # | |
16 | # This program is distributed in the hope that it will be useful, | |
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | # GNU General Public License for more details. | |
20 | # | |
21 | # You should have received a copy of the GNU General Public License | |
22 | # along with this program; if not, write to the Free Software | |
23 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
24 | # MA 02110-1301, USA. | |
25 | # | |
26 | # | |
27 | ||
28 | ||
29 | ||
30 | ||
31 | # These two lines are only needed if you don't put the script directly into | |
32 | # the installation directory (and only works for linux :() | |
33 | #import sys | |
34 | #sys.path.append('/usr/share/inkscape/extensions') | |
35 | ||
36 | # We will use the inkex module with the predefined Effect base class. | |
37 | import inkex | |
38 | # The simplestyle module provides functions for style parsing. | |
39 | from simplestyle import * | |
40 | import math, re, nicechart_colors as nc_colors | |
41 | import sys # for debugging | |
42 | ||
43 | csv_file_name="" | |
44 | logf = open('C:\temp\log.txt', 'w') | |
45 | ||
46 | class NiceChart(inkex.Effect): | |
47 | """ Create a bar, pie, or stacked chart from list of named data """ | |
48 | ||
49 | def __init__(self): | |
50 | # Call the base class constructor. | |
51 | inkex.Effect.__init__(self) | |
52 | ||
53 | # Define string option "--what" with "-w" shortcut and default chart values. | |
54 | self.OptionParser.add_option('-w', '--what', action = 'store', | |
55 | type = 'string', dest = 'what', default = '22,11,67', | |
56 | help = 'Chart Values') | |
57 | ||
58 | # Define string option "--type" with "-t" shortcut. | |
59 | self.OptionParser.add_option("-t", "--type", action="store", | |
60 | type="string", dest="type", default='', | |
61 | help="Chart Type") | |
62 | ||
63 | # Define bool option "--blur" with "-b" shortcut. | |
64 | self.OptionParser.add_option("-b", "--blur", action="store", | |
65 | type="inkbool", dest="blur", default='True', | |
66 | help="Blur Type") | |
67 | ||
68 | # Define string option "--file" with "-f" shortcut. | |
69 | self.OptionParser.add_option("-f", "--filename", action="store", | |
70 | type="string", dest="filename", default='', | |
71 | help="Name of File") | |
72 | ||
73 | # Define string option "--input_type" with "-i" shortcut. | |
74 | self.OptionParser.add_option("-i", "--input_type", action="store", | |
75 | type="string", dest="input_type", default='file', | |
76 | help="Chart Type") | |
77 | ||
78 | # Define string option "--delimiter" with "-d" shortcut. | |
79 | self.OptionParser.add_option("-d", "--delimiter", action="store", | |
80 | type="string", dest="csv_delimiter", default=';', | |
81 | help="delimiter") | |
82 | ||
83 | # Define string option "--colors" with "-c" shortcut. | |
84 | self.OptionParser.add_option("-c", "--colors", action="store", | |
85 | type="string", dest="colors", default='default', | |
86 | help="color-scheme") | |
87 | ||
88 | self.OptionParser.add_option("", "--reverse_colors", action="store", | |
89 | type="inkbool", dest="reverse_colors", default='False', | |
90 | help="reverse color-scheme") | |
91 | ||
92 | self.OptionParser.add_option("-k", "--col_key", action="store", | |
93 | type="int", dest="col_key", default='0', | |
94 | help="column that contains the keys") | |
95 | ||
96 | ||
97 | self.OptionParser.add_option("-v", "--col_val", action="store", | |
98 | type="int", dest="col_val", default='1', | |
99 | help="column that contains the values") | |
100 | ||
101 | self.OptionParser.add_option("-r", "--rotate", action="store", | |
102 | type="inkbool", dest="rotate", default='False', | |
103 | help="Draw barchart horizontally") | |
104 | ||
105 | self.OptionParser.add_option("-W", "--bar-width", action="store", | |
106 | type="int", dest="bar_width", default='10', | |
107 | help="width of bars") | |
108 | ||
109 | self.OptionParser.add_option("-p", "--pie-radius", action="store", | |
110 | type="int", dest="pie_radius", default='100', | |
111 | help="radius of pie-charts") | |
112 | ||
113 | self.OptionParser.add_option("-q", "--pie-offset", action="store", | |
114 | type="int", dest="pie_offset", default='0', | |
115 | help="Rotation offset of pie-charts") | |
116 | ||
117 | self.OptionParser.add_option("-H", "--bar-height", action="store", | |
118 | type="int", dest="bar_height", default='100', | |
119 | help="height of bars") | |
120 | ||
121 | self.OptionParser.add_option("-O", "--bar-offset", action="store", | |
122 | type="int", dest="bar_offset", default='5', | |
123 | help="distance between bars") | |
124 | ||
125 | self.OptionParser.add_option("-l", "--labels", action="store", | |
126 | type="inkbool", dest="labels", default='False', | |
127 | help="Initial line is a description") | |
128 | ||
129 | self.OptionParser.add_option("", "--stroke-width", action="store", | |
130 | type="int", dest="stroke_width", default='2') | |
131 | ||
132 | self.OptionParser.add_option("-o", "--text-offset", action="store", | |
133 | type="int", dest="text_offset", default='5', | |
134 | help="distance between bar and descriptions") | |
135 | ||
136 | self.OptionParser.add_option("-F", "--font", action="store", | |
137 | type="string", dest="font", default='sans-serif', | |
138 | help="font of description") | |
139 | ||
140 | self.OptionParser.add_option("-S", "--font-size", action="store", | |
141 | type="int", dest="font_size", default='10', | |
142 | help="font size of description") | |
143 | ||
144 | self.OptionParser.add_option("-C", "--font-color", action="store", | |
145 | type="string", dest="font_color", default='black', | |
146 | help="font color of description") | |
147 | #Dummy: | |
148 | self.OptionParser.add_option("","--input_sections") | |
149 | ||
150 | ||
151 | def create_layer(self, title): | |
152 | """ Create a new (top level) named layer to put charts into """ | |
153 | svg = self.document.getroot() | |
154 | layer = inkex.etree.SubElement(svg, 'g') | |
155 | layer.set(inkex.addNS('label', 'inkscape'), 'Chart-Layer: %s' % (title)) | |
156 | layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer') | |
157 | return layer | |
158 | ||
159 | def create_blur_filter(self): | |
160 | """ Create a single reuseable filter for blur effect | |
161 | - gaussian blur | |
162 | """ | |
163 | xoffset = str(-0.5) # get from options | |
164 | yoffset = str(-0.5) | |
165 | blur_factor = str(1.1) | |
166 | # Get defs of Document | |
167 | defs = self.xpathSingle('/svg:svg//svg:defs') | |
168 | if defs == None: | |
169 | defs = inkex.etree.SubElement(self.document.getroot(),inkex.addNS('defs','svg')) | |
170 | ||
171 | # Create new Filter | |
172 | filt = inkex.etree.SubElement(defs,inkex.addNS('filter','svg')) | |
173 | filtId = self.uniqueId('filter') | |
174 | self.filtId = 'filter:url(#%s);' % filtId | |
175 | for k, v in [ ('id', filtId), ('height', "3"), ('width', "3"), | |
176 | ('x', xoffset), ('y', yoffset) ]: | |
177 | filt.set(k, v) | |
178 | ||
179 | # Append Gaussian Blur to that Filter | |
180 | fe = inkex.etree.SubElement(filt,inkex.addNS('feGaussianBlur','svg')) | |
181 | fe.set('stdDeviation', blur_factor) | |
182 | return filtId | |
183 | ||
184 | def get_colors(self): | |
185 | """ get colors directly from UI, or use premade named sets | |
186 | - ideally use a dropdown to select premade sets. | |
187 | - ideally sample gradients to get color sets | |
188 | """ | |
189 | colors=self.options.colors # make copy so reverse operation is not persistent | |
190 | if(colors[0].isalpha()): | |
191 | colors=nc_colors.get_color_scheme(colors) | |
192 | else: | |
193 | colors=re.findall("(#[0-9a-fA-F]{6})",colors) | |
194 | #to be sure we create a fallback: | |
195 | if(len(colors)==0): | |
196 | colors=nc_colors.get_color_scheme() | |
197 | # reverse ? | |
198 | colors = [c for c in colors] # safe copy | |
199 | if(self.options.reverse_colors): | |
200 | colors.reverse() | |
201 | return (colors, len(colors)) | |
202 | ||
203 | ||
204 | def make_chart(self, charttype, keys, values, title, blur_filter, layer=False ): | |
205 | logf.write("Create Chart: %s %s\n" % (charttype, title)) | |
206 | logf.write(" layer : %s\n" % (layer)) | |
207 | logf.write(" keys,values : %s %s\n" % (keys, values)) | |
208 | # | |
209 | keys_present=False | |
210 | if keys: keys_present=True | |
211 | ||
212 | # Get access to main SVG document element and get its dimensions. | |
213 | svg = self.document.getroot() | |
214 | ||
215 | # Get the page attibutes for positioning | |
216 | width = inkex.unittouu(svg.get('width')) | |
217 | height = inkex.unittouu(svg.attrib['height']) | |
218 | ||
219 | if layer == False: | |
220 | # no layer supplied so make one (means its a direct single chart) | |
221 | layer = self.create_layer(title) | |
222 | # make group to hold this chart | |
223 | group = inkex.etree.SubElement(layer, 'g', {inkex.addNS('label','inkscape'):title }) | |
224 | ||
225 | # Get Colors | |
226 | Colors, color_count = self.get_colors() | |
227 | logf.write(" Colors: %s\n" % Colors) | |
228 | # | |
229 | bar_height=self.options.bar_height | |
230 | bar_width=self.options.bar_width | |
231 | bar_offset=self.options.bar_offset | |
232 | #offset of the description in stacked-bar-charts: | |
233 | text_offset=self.options.text_offset | |
234 | ||
235 | #get font | |
236 | font=self.options.font | |
237 | font_size=self.options.font_size | |
238 | font_color=self.options.font_color | |
239 | ||
240 | #get bar chart orientation | |
241 | rotate = self.options.rotate | |
242 | ||
243 | pie_radius = self.options.pie_radius | |
244 | pie_offset = self.options.pie_offset * 2*math.pi/360 # degrees to radians | |
245 | stroke_width = self.options.stroke_width | |
246 | ||
247 | ||
248 | if charttype=="bar" : | |
249 | ######### | |
250 | ###BAR### | |
251 | ######### | |
252 | ||
253 | #iterate all values, use offset to draw the bars in different places | |
254 | offset=0 | |
255 | count=0 | |
256 | ||
257 | # Normalize the bars to the largest value | |
258 | try: | |
259 | value_max=max(values) | |
260 | except ValueError: | |
261 | value_max=0.0 | |
262 | ||
263 | for i in range(len(values)): | |
264 | values[i]=(values[i]/value_max)*bar_height | |
265 | ||
266 | ||
267 | # Draw Single bars with their shadows | |
268 | for value in values: | |
269 | ||
270 | #draw blur, if it is wanted | |
271 | if blur_filter : | |
272 | # Create shadow element | |
273 | shadow = inkex.etree.SubElement(group,inkex.addNS("rect","svg")) | |
274 | # Set chart position to center of document. Make it horizontal or vertical | |
275 | if not rotate : | |
276 | shadow.set('x', str(width / 2 + offset +1)) | |
277 | shadow.set('y', str(height / 2 - int(value)+1)) | |
278 | else: | |
279 | shadow.set('y', str(width / 2 + offset +1)) | |
280 | shadow.set('x', str(height / 2 +1)) | |
281 | # Set shadow properties | |
282 | if not rotate : | |
283 | shadow.set("width", str(bar_width)) | |
284 | shadow.set("height", str(int(value))) | |
285 | else: | |
286 | shadow.set("height", str(bar_width)) | |
287 | shadow.set("width", str(int(value))) | |
288 | # Set shadow blur (connect to filter object in xml path) | |
289 | shadow.set("style","filter:url(#%s)" % blur_filter) | |
290 | ||
291 | # Create rectangle element | |
292 | rect = inkex.etree.SubElement(group,inkex.addNS('rect','svg')) | |
293 | ||
294 | # Set chart position to center of document. | |
295 | if(not rotate): | |
296 | rect.set('x', str(width/2+offset)) | |
297 | rect.set('y', str(height/2-int(value))) | |
298 | else: | |
299 | rect.set('y', str(width/2+offset)) | |
300 | rect.set('x', str(height/2)) | |
301 | # Set rectangle properties | |
302 | if(not rotate): | |
303 | rect.set("width", str(bar_width)) | |
304 | rect.set("height", str(int(value))) | |
305 | else: | |
306 | rect.set("height", str(bar_width)) | |
307 | rect.set("width", str(int(value))) | |
308 | ||
309 | rect.set("style","fill:"+Colors[count%color_count]) | |
310 | ||
311 | # Set shadow blur (connect to filter object in xml path) | |
312 | if(blur_filter): | |
313 | shadow.set("style","filter:url(#%s)" % blur_filter) | |
314 | ||
315 | # If keys are given create text elements | |
316 | if(keys_present): | |
317 | text = inkex.etree.SubElement(group, inkex.addNS('text','svg')) | |
318 | if(not rotate): #=vertical | |
319 | text.set("transform","matrix(0,-1,1,0,0,0)") | |
320 | #y after rotation: | |
321 | text.set("x", "-"+str(height/2+text_offset)) | |
322 | #x after rotation: | |
323 | text.set("y", str(width/2+offset+bar_width/2+font_size/3)) | |
324 | else: #=horizontal | |
325 | text.set("y", str(width/2+offset+bar_width/2+font_size/3)) | |
326 | text.set("x", str(height/2-text_offset)) | |
327 | ||
328 | text.set("style","font-size:"+str(font_size)\ | |
329 | +"px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:"\ | |
330 | +font+";-inkscape-font-specification:Bitstream Charter;text-align:end;text-anchor:end;fill:"\ | |
331 | +font_color) | |
332 | ||
333 | text.text=keys[count] | |
334 | ||
335 | # Increase Offset and Color | |
336 | offset=offset+bar_width+bar_offset | |
337 | count += 1 | |
338 | #End Bar | |
339 | ||
340 | ||
341 | ||
342 | elif(charttype=="pie"): | |
343 | ######### | |
344 | ###PIE### | |
345 | ######### | |
346 | # Iterate all values to draw the different slices | |
347 | count=0 | |
348 | ||
349 | # Add a grey background circle | |
350 | background=inkex.etree.SubElement(group, inkex.addNS("circle","svg")) | |
351 | background.set("cx", str(width/2)) | |
352 | background.set("cy", str(height/2)) | |
353 | background.set("r", str(pie_radius)) | |
354 | background.set("style","fill:#aaaaaa;stroke:none") | |
355 | logf.write(" made BG\n") | |
356 | #create value sum in order to divide the slices | |
357 | try: | |
358 | valuesum=sum(values) | |
359 | except ValueError: | |
360 | valuesum=0 | |
361 | ||
362 | # Set an offsetangle | |
363 | offset=pie_offset | |
364 | logf.write(" valuesum, offset =%s %s:\n" % (valuesum, offset)) | |
365 | # Draw single slices with their shadow | |
366 | for value in values: | |
367 | # Calculate the PI-angles for start and end | |
368 | angle=(2*math.pi)/valuesum*float(value) | |
369 | ||
370 | # Create the shadow first (if it should be created): | |
371 | if(blur_filter): | |
372 | shadow=inkex.etree.SubElement(group, inkex.addNS("path","svg")) | |
373 | shadow.set(inkex.addNS('type', 'sodipodi'), 'arc') | |
374 | shadow.set(inkex.addNS('cx', 'sodipodi'), str(width/2)) | |
375 | shadow.set(inkex.addNS('cy', 'sodipodi'), str(height/2)) | |
376 | shadow.set(inkex.addNS('rx', 'sodipodi'), str(pie_radius)) | |
377 | shadow.set(inkex.addNS('ry', 'sodipodi'), str(pie_radius)) | |
378 | shadow.set(inkex.addNS('start', 'sodipodi'), str(offset)) | |
379 | shadow.set(inkex.addNS('end', 'sodipodi'), str(offset+angle)) | |
380 | shadow.set("style","filter:url(#%s);fill:#000000" % blur_filter) | |
381 | ||
382 | #then add the slice | |
383 | pieslice=inkex.etree.SubElement(group, inkex.addNS("path","svg")) | |
384 | pieslice.set(inkex.addNS('type', 'sodipodi'), 'arc') | |
385 | pieslice.set(inkex.addNS('cx', 'sodipodi'), str(width/2)) | |
386 | pieslice.set(inkex.addNS('cy', 'sodipodi'), str(height/2)) | |
387 | pieslice.set(inkex.addNS('rx', 'sodipodi'), str(pie_radius)) | |
388 | pieslice.set(inkex.addNS('ry', 'sodipodi'), str(pie_radius)) | |
389 | pieslice.set(inkex.addNS('start', 'sodipodi'), str(offset)) | |
390 | pieslice.set(inkex.addNS('end', 'sodipodi'), str(offset+angle)) | |
391 | pieslice.set("style","fill:"+Colors[count%color_count]+";stroke:none;fill-opacity:1") | |
392 | ||
393 | #If text is given, draw short paths and add the text | |
394 | if(keys_present): | |
395 | path=inkex.etree.SubElement(group, inkex.addNS("path","svg")) | |
396 | path.set("d","m "+str((width/2)+pie_radius*math.cos(angle/2+offset))+","+str((height/2)+pie_radius*math.sin(angle/2+offset))+" "+str((text_offset-2)*math.cos(angle/2+offset))+","+str((text_offset-2)*math.sin(angle/2+offset))) | |
397 | path.set("style","fill:none;stroke:"+font_color+";stroke-width:"+str(stroke_width)+"px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1") | |
398 | text=inkex.etree.SubElement(group, inkex.addNS("text","svg")) | |
399 | text.set("x", str((width/2)+(pie_radius+text_offset)*math.cos(angle/2+offset))) | |
400 | text.set("y", str((height/2)+(pie_radius+text_offset)*math.sin(angle/2+offset)+font_size/3)) | |
401 | textstyle="font-size:"+str(font_size)+"px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:"+font+";-inkscape-font-specification:Bitstream Charter;fill:"+font_color | |
402 | #check if it is right or left of the Pie | |
403 | if(math.cos(angle/2+offset)>0): | |
404 | text.set("style",textstyle) | |
405 | else: | |
406 | text.set("style",textstyle+";text-align:end;text-anchor:end") | |
407 | text.text=keys[count] | |
408 | ||
409 | #increase the rotation-offset and the colorcycle-position | |
410 | offset += angle | |
411 | count += 1 | |
412 | logf.write(" chart: offset,color =%s %s %s:\n" % (offset, count, count%color_count)) | |
413 | ||
414 | #End Pie | |
415 | ||
416 | ||
417 | ||
418 | ||
419 | elif(charttype=="stbar"): | |
420 | ################# | |
421 | ###STACKED BAR### | |
422 | ################# | |
423 | # Iterate all values to draw the different slices | |
424 | count=0 | |
425 | ||
426 | #create value sum in order to divide the bars | |
427 | try: | |
428 | valuesum=sum(values) | |
429 | except ValueError: | |
430 | valuesum=0.0 | |
431 | ||
432 | for value in values: | |
433 | valuesum=valuesum+float(value) | |
434 | ||
435 | # Init offset | |
436 | offset=0 | |
437 | stack_index = len(values)-1 #loopcounter | |
438 | ||
439 | # Draw Single bars with their shadows | |
440 | for value in values: | |
441 | ||
442 | # Calculate the individual heights normalized on 100units | |
443 | normedvalue=(bar_height/valuesum)*float(value) | |
444 | ||
445 | if(blur_filter): | |
446 | # Create rectangle element | |
447 | shadow = inkex.etree.SubElement(group,inkex.addNS("rect","svg")) | |
448 | # Set chart position to center of document. | |
449 | if(not rotate): | |
450 | shadow.set('x', str(width / 2 + 1)) | |
451 | shadow.set('y', str(height / 2 - offset - (normedvalue)+1)) | |
452 | else: | |
453 | shadow.set('x', str(width / 2 + 1 + offset)) | |
454 | shadow.set('y', str(height / 2 +1)) | |
455 | # Set rectangle properties | |
456 | if(not rotate): | |
457 | shadow.set("width",str(bar_width)) | |
458 | shadow.set("height", str((normedvalue))) | |
459 | else: | |
460 | shadow.set("width",str((normedvalue))) | |
461 | shadow.set("height", str(bar_width)) | |
462 | # Set shadow blur (connect to filter object in xml path) | |
463 | shadow.set("style","filter:url(#%s)" % blur_filter) | |
464 | ||
465 | # Create rectangle element | |
466 | rect = inkex.etree.SubElement(group,inkex.addNS('rect','svg')) | |
467 | ||
468 | # Set chart position to center of document. | |
469 | if( not rotate ): | |
470 | rect.set('x', str(width / 2 )) | |
471 | rect.set('y', str(height / 2 - offset - (normedvalue))) | |
472 | else: | |
473 | rect.set('x', str(width / 2 + offset )) | |
474 | rect.set('y', str(height / 2 )) | |
475 | # Set rectangle properties | |
476 | if( not rotate ): | |
477 | rect.set("width", str(bar_width)) | |
478 | rect.set("height", str((normedvalue))) | |
479 | else: | |
480 | rect.set("height", str(bar_width)) | |
481 | rect.set("width", str((normedvalue))) | |
482 | rect.set("style","fill:"+Colors[count%color_count]) | |
483 | ||
484 | #If text is given, draw short paths and add the text | |
485 | if(keys_present): | |
486 | if(not rotate): | |
487 | path=inkex.etree.SubElement(group,inkex.addNS("path","svg")) | |
488 | path.set("d","m "+str((width+bar_width)/2)+","+str(height / 2 - offset - (normedvalue / 2))+" "+str(bar_width/2+text_offset)+",0") | |
489 | path.set("style","fill:none;stroke:"+font_color+";stroke-width:"+str(stroke_width)+"px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1") | |
490 | text = inkex.etree.SubElement(group,inkex.addNS('text','svg')) | |
491 | text.set("x", str(width/2+bar_width+text_offset+1)) | |
492 | text.set("y", str(height / 2 - offset + font_size/3 - (normedvalue / 2))) | |
493 | text.set("style","font-size:"+str(font_size)+"px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:"+font+";-inkscape-font-specification:Bitstream Charter;fill:"+font_color) | |
494 | text.text=keys[color] | |
495 | else: | |
496 | path=inkex.etree.SubElement(group,inkex.addNS("path","svg")) | |
497 | path.set("d","m "+str((width)/2+offset+normedvalue/2)+"," | |
498 | +str(height / 2 + bar_width/2) | |
499 | +" 0,"+str(bar_width/2+(font_size*stack_index)+text_offset)) #line | |
500 | path.set("style","fill:none;stroke:"+font_color+";stroke-width:"+str(stroke_width)+"px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1") | |
501 | text = inkex.etree.SubElement(group,inkex.addNS('text','svg')) | |
502 | text.set("x", str((width)/2+offset+normedvalue/2-font_size/3)) | |
503 | text.set("y", str((height/2)+bar_width+(font_size*(stack_index+1))+text_offset )) | |
504 | text.set("style","font-size:"+str(font_size)+"px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:"+font+";-inkscape-font-specification:Bitstream Charter;fill:"+font_color) | |
505 | text.text=keys[count] | |
506 | ||
507 | # Increase Offset and Color | |
508 | offset=offset+normedvalue | |
509 | count += 1 | |
510 | ||
511 | stack_index-=1 #loopcounter | |
512 | ||
513 | ||
514 | def extract_data(self, csv_data): | |
515 | """ Expecting csv data in one of several forms. | |
516 | - first line has descriptions or not. | |
517 | If so then first is title of graph, subsequent column headers are labels | |
518 | - if more than one column after col_val then treat as new charts | |
519 | - blank line is separator. new layer of graphs. | |
520 | """ | |
521 | data = [] | |
522 | csv_delimiter=self.options.csv_delimiter | |
523 | col_key=self.options.col_key | |
524 | col_val=self.options.col_val | |
525 | desc = self.options.labels | |
526 | # | |
527 | label = "" | |
528 | titles = [] | |
529 | keys = [] | |
530 | values = [] | |
531 | started = False | |
532 | for line in csv_data: | |
533 | line = line.strip() | |
534 | items = line.split(csv_delimiter) | |
535 | size = len(items) | |
536 | sys.stderr.write(" items: %s\n" % items) | |
537 | foo = [i for i in items] # cheap copy | |
538 | # remove dummy if all lines not same length (thanks XLS!!) | |
539 | if size > 1: | |
540 | count = foo.count('') | |
541 | for i in range(count): | |
542 | foo.remove('') | |
543 | items = foo | |
544 | size = len(items) | |
545 | sys.stderr.write("Line = %d, %s\n"% (size,items)) | |
546 | if items[0] == '': | |
547 | # blank line so start again | |
548 | if not started: | |
549 | pass | |
550 | else: | |
551 | #we have something to save so we can start a new chart. | |
552 | data.append([titles,keys,values]) | |
553 | keys = [] | |
554 | values = [] | |
555 | started = False | |
556 | sys.stderr.write("Saving chart, false start\n") | |
557 | else: # process valid lines | |
558 | if not started and desc: | |
559 | sys.stderr.write("Grab labels\n") | |
560 | # first line so grab labels if applicable | |
561 | label = items[0] | |
562 | if len(items)>1: | |
563 | titles = [label, items[col_val:]] | |
564 | for i in range(len(items[col_val:])): | |
565 | values.append([]) | |
566 | sys.stderr.write(" values: %s\n" % values) | |
567 | else: # process line normally | |
568 | sys.stderr.write("process line %s\n" % items[col_key]) | |
569 | keys.append(items[col_key]) | |
570 | for idx,item in enumerate(items[col_val:]): | |
571 | #sys.stderr.write(" idx,item: %s %s = %s %s\n" % (idx,item, values[idx], values)) | |
572 | values[idx].append(float(item.replace('$','').replace('%',''))) | |
573 | sys.stderr.write(" values=%s\n" % values) | |
574 | # | |
575 | started = True # started a block (we may have already started). | |
576 | sys.stderr.write(" started=True\n") | |
577 | # all done so store last set of charts | |
578 | if values != []: | |
579 | data.append([titles,keys,values]) | |
580 | titles = [] | |
581 | keys = [] | |
582 | values = [] | |
583 | return data | |
584 | ||
585 | ||
586 | ||
587 | def effect(self): | |
588 | """ | |
589 | Effect behaviour. | |
590 | Overrides base class' method and inserts a nice looking chart into SVG document. | |
591 | """ | |
592 | # Get script's "--what" option value and process the data type --- i confess the if term is a little bit of magic | |
593 | what = self.options.what | |
594 | keys=[] | |
595 | values=[] | |
596 | ||
597 | csv_file_name=self.options.filename | |
598 | csv_delimiter=self.options.csv_delimiter | |
599 | input_type=self.options.input_type | |
600 | col_key=self.options.col_key | |
601 | col_val=self.options.col_val | |
602 | ||
603 | blur_filter = False | |
604 | if self.options.blur: | |
605 | blur_filter = self.create_blur_filter() | |
606 | ||
607 | if(input_type=="\"file\""): | |
608 | csv_file=open(csv_file_name,"r") | |
609 | csv_data = csv_file.readlines() | |
610 | csv_file.close() | |
611 | data = self.extract_data(csv_data) | |
612 | # Get script's "--type" option value. | |
613 | charttype=self.options.type | |
614 | logf.write("\nData: %s\n\n" % data) | |
615 | unique = 0 | |
616 | for (titles,keys,values) in data: | |
617 | # make chart for each piece of data | |
618 | layer = self.create_layer(titles[0]) | |
619 | for idx,dataset in enumerate(values): | |
620 | sys.stderr.write("\nMaking: %d %s\n %s\n %s\n" % (idx, charttype, keys, dataset)) | |
621 | logf.write("\nMaking: %d %s\n %s\n %s\n" % (idx, charttype, keys, dataset)) | |
622 | #if unique == 0: | |
623 | self.make_chart(charttype, keys, dataset, titles[1][idx]+"_"+str(unique), blur_filter, layer) | |
624 | unique += 1 | |
625 | elif(input_type=="\"direct_input\""): | |
626 | what=re.findall("([A-Z|a-z|0-9]+:[0-9]+\.?[0-9]*)",what) | |
627 | for value in what: | |
628 | value=value.split(":") | |
629 | keys.append(value[0]) | |
630 | values.append(float(value[1])) | |
631 | # Get script's "--type" option value. | |
632 | charttype=self.options.type | |
633 | # | |
634 | self.make_chart(charttype, keys, values, self.options.what) | |
635 | ||
636 | ||
637 | ||
638 | # Create effect instance and apply it. | |
639 | effect = NiceChart() | |
640 | effect.affect() |