diff options
| author | Martin Owens <doctormo@gmail.com> | 2016-02-27 17:38:36 +0000 |
|---|---|---|
| committer | Martin Owens <doctormo@gmail.com> | 2016-02-27 17:38:36 +0000 |
| commit | 380741fc91724ef40be2b6d938fd3d991bb28ccb (patch) | |
| tree | 5cd7b86175e92733920a1c62ec306896fe899e27 | |
| parent | Fix miter-limit behavior to match SVG spec. (diff) | |
| download | inkscape-380741fc91724ef40be2b6d938fd3d991bb28ccb.tar.gz inkscape-380741fc91724ef40be2b6d938fd3d991bb28ccb.zip | |
Remove Maren from AUTHORS (on request) and add nicecharts from upstream where it was abandoned
(bzr r14672)
| -rw-r--r-- | AUTHORS | 1 | ||||
| -rw-r--r-- | share/extensions/nicechart.inx | 103 | ||||
| -rwxr-xr-x | share/extensions/nicechart.py | 718 |
3 files changed, 821 insertions, 1 deletions
@@ -65,7 +65,6 @@ Toine de Greef Michael Grosberg Bryce Harrington Dale Harvey -Maren Hachmann Aurélio Adnauer Heckert Carl Hetherington Jos Hirth diff --git a/share/extensions/nicechart.inx b/share/extensions/nicechart.inx new file mode 100644 index 000000000..0ebcd8fca --- /dev/null +++ b/share/extensions/nicechart.inx @@ -0,0 +1,103 @@ +<!-- + nicechart.inx + + Copyright 2011-2016 + + Christoph Sterz + Florian Weber + Maren Hachmann + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. +--> + +<inkscape-extension> + <_name>NiceCharts</_name> + <id>org.inkscape.filter.nicechart</id> + <dependency type="executable" location="extensions">nicechart.py</dependency> + <dependency type="executable" location="extensions">inkex.py</dependency> + <param name="input_sections" type="notebook"> + <page name="data_settings" _gui-text="Data"> + <param name="input_type" type="notebook"> + <page name="file" _gui-text="Data from file"> + <_param name="desc" type="description">Enter the full path to a CSV file:</_param> + <param name="filename" type="string" _gui-text="File:"></param> + <param name="delimiter" type="string" _gui-text="Delimiter:">;</param> + <param name="col_key" type="int" _gui-text="Column that contains the keys:" min="0" max="10000">0</param> + <param name="col_val" type="int" _gui-text="Column that contains the values:" min="0" max="10000">1</param> + <param name="encoding" type="string" _gui-text="File encoding (e.g. utf-8):">utf-8</param> + <param name="headings" type="boolean" _gui-text="First line contains headings">false</param> + </page> + <page name="direct_input" _gui-text="Direct input"> + <_param name="desc" type="description">Type in comma separated values:</_param> + <_param name="desc" type="description">(format like this: apples:3,bananas:5)</_param> + <param name="what" type="string" _gui-text="Data:">apples:3,bananas:5,oranges:10,pears:4</param> + </page> + </param> + </page> + <page name="description_settings" _gui-text="Labels"> + <param type="string" name="font" _gui-text="Font:">sans-serif</param> + <param type="int" name="font-size" _gui-text="Font size:" min="0" max="10000">10</param> + <param type="string" name="font-color" _gui-text="Font color:">#000000</param> + </page> + <page name="chart_settings" _gui-text="Charts"> + <param type="boolean" name="rotate" _gui-text="Draw horizontally">false</param> + <param name="bar-height" type="int" _gui-text="Bar length:" min="0" max="100000">100</param> + <param name="bar-width" type="int" _gui-text="Bar width:" min="0" max="100000">10</param> + <param name="pie-radius" type="int" _gui-text="Pie radius:" min="0" max="100000">100</param> + <param name="bar-offset" type="int" _gui-text="Bar offset:" min="0" max="100000">5</param> + <param name="stroke-width" type="float" min="0.1" max="100000.0" precision="2" _gui-text="Stroke width:">1</param> + <param name="text-offset" type="int" _gui-text="Offset between chart and labels:" min="0" max="100000">5</param> + <param name="heading-offset" type="int" _gui-text="Offset between chart and chart title:" min="-100000" max="100000">50</param> + <param name="segment-overlap" type="boolean" _gui-text="Work around aliasing effects (creates overlapping segments)">false</param> + + <param name="colors" type="enum" _gui-text="Color scheme:"> + <_item value="default">Default</_item> + <_item value="blue">Blue</_item> + <_item value="gray">Gray</_item> + <_item value="contrast">Contrast</_item> + <_item value="sap">SAP</_item> + </param> + + <param type="string" name="colors_override" _gui-text="Custom colors:"></param> + + <param type="boolean" name="reverse_colors" _gui-text="Reverse color scheme">false</param> + <param type="boolean" name="blur" _gui-text="Drop shadow">false</param> + </page> + <page name="value_settings" _gui-text="Values"> + <param type="boolean" name="show_values" _gui-text="Show values">false</param> +<!-- <param type="string" name="font" _gui-text="Font:">sans-serif</param> + <param type="int" name="font-size" _gui-text="Font size:" min="0" max="10000">10</param> + <param type="description" name="desc">Font color:</param> + <param type="string" name="font-color" _gui-text="Font color:">#000000</param> +--> + </page> + </param> + <param name="type" type="enum" _gui-text="Chart type:"> + <_item value="bar">Bar chart</_item> + <_item value="pie">Pie chart</_item> + <_item value="pie_abs">Pie chart (percentage)</_item> + <_item value="stbar">Stacked bar chart</_item> + </param> + <effect> + <object-type>all</object-type> + <effects-menu> + <submenu _name="Render"/> + </effects-menu> + </effect> + <script> + <command reldir="extensions" interpreter="python">nicechart.py</command> + </script> +</inkscape-extension> diff --git a/share/extensions/nicechart.py b/share/extensions/nicechart.py new file mode 100755 index 000000000..d4a819ccc --- /dev/null +++ b/share/extensions/nicechart.py @@ -0,0 +1,718 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# nicechart.py +# +# Copyright 2011-2016 +# +# Christoph Sterz +# Florian Weber +# Maren Hachmann +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +# TODO / Ideas: +# allow negative values for bar charts +# show values for stacked bar charts +# don't create a new layer for each chart, but a normal group +# correct bar height for stacked bars (it's only half as high as it should be, double) +# adjust position of heading +# use aliasing workaround for stacked bars (e.g. let the rectangles overlap) + +# Example CSV file contents: +''' +Month;1978;1979;1980;1981 +January;2;1,3;0.1;2.3 +February;6.5;2.4;1.2;6.1 +March;7.4;6.7;7.9;4.7 +April;7.7;6.4;8.2;8.9 +May;10.9;11.7;18.7;11.1 +June;12.6;14.2;14.7;14.7 +July;16.5;15.5;17.5;15.1 +August;15.9;15.4;14.6;16.6 +September;14;14.5;13.2;15.3 +October;11.9;13.9;11.5;9.2 +November;6.7;8.5;7;6.6 +December;6.4;2.2;6.3;3.5 +''' +# The extension creates one chart for a single value column in one go, +# e.g. chart all temperatures for all months of the year 1978 into one chart. +# (for this, select column 0 for labels and column 1 for values). +# "1978" etc. can be used as heading (Need not be numeric. If not used delete the heading line.) +# Month names can be used as labels +# Values can be shown, in addition to labels (doesn't work with stacked bar charts) +# Values can contain commas as decimal separator, as long as delimiter isn't comma +# Negative values are not yet supported. + + +import re +import sys +import math +import inkex + +from simplestyle import * + +#www.sapdesignguild.org/goodies/diagram_guidelines/color_palettes.html#mss +COLOUR_TABLE = { + "red": ["#460101", "#980101", "#d40000", "#f44800", "#fb8b00", "#eec73e", "#d9bb7a", "#fdd99b"], + "blue": ["#000442", "#0F1781", "#252FB7", "#3A45E1", "#656DDE", "#8A91EC"], + "gray": ["#222222", "#444444", "#666666", "#888888", "#aaaaaa", "#cccccc", "#eeeeee"], + "contrast": ["#0000FF", "#FF0000", "#00FF00", "#CF9100", "#FF00FF", "#00FFFF"], + "sap": ["#f8d753", "#5c9746", "#3e75a7", "#7a653e", "#e1662a", "#74796f", "#c4384f", + "#fff8a3", "#a9cc8f", "#b2c8d9", "#bea37a", "#f3aa79", "#b5b5a9", "#e6a5a5"] +} + +def get_color_scheme(name="default"): + return COLOUR_TABLE.get(name.lower(), COLOUR_TABLE['red']) + + +class NiceChart(inkex.Effect): + """ + Inkscape extension that can draw pie charts and bar charts + (stacked, single, horizontally or vertically) + with optional drop shadow, from a csv file or from pasted text + """ + + def __init__(self): + """ + Constructor. + Defines the "--what" option of a script. + """ + # Call the base class constructor. + inkex.Effect.__init__(self) + + # Define string option "--what" with "-w" shortcut and default chart values. + self.OptionParser.add_option('-w', '--what', action='store', + type='string', dest='what', default='22,11,67', + help='Chart Values') + + # Define string option "--type" with "-t" shortcut. + self.OptionParser.add_option("-t", "--type", action="store", + type="string", dest="type", default='', + help="Chart Type") + + # Define bool option "--blur" with "-b" shortcut. + self.OptionParser.add_option("-b", "--blur", action="store", + type="inkbool", dest="blur", default='True', + help="Blur Type") + + # Define string option "--file" with "-f" shortcut. + self.OptionParser.add_option("-f", "--filename", action="store", + type="string", dest="filename", default='', + help="Name of File") + + # Define string option "--input_type" with "-i" shortcut. + self.OptionParser.add_option("-i", "--input_type", action="store", + type="string", dest="input_type", default='file', + help="Chart Type") + + # Define string option "--delimiter" with "-d" shortcut. + self.OptionParser.add_option("-d", "--delimiter", action="store", + type="string", dest="csv_delimiter", default=';', + help="delimiter") + + # Define string option "--colors" with "-c" shortcut. + self.OptionParser.add_option("-c", "--colors", action="store", + type="string", dest="colors", default='default', + help="color-scheme") + + # Define string option "--colors_override" + self.OptionParser.add_option("", "--colors_override", action="store", + type="string", dest="colors_override", default='', + help="color-scheme-override") + + + self.OptionParser.add_option("", "--reverse_colors", action="store", + type="inkbool", dest="reverse_colors", default='False', + help="reverse color-scheme") + + self.OptionParser.add_option("-k", "--col_key", action="store", + type="int", dest="col_key", default='0', + help="column that contains the keys") + + + self.OptionParser.add_option("-v", "--col_val", action="store", + type="int", dest="col_val", default='1', + help="column that contains the values") + + self.OptionParser.add_option("", "--encoding", action="store", + type="string", dest="encoding", default='utf-8', + help="encoding of the CSV file, e.g. utf-8") + + self.OptionParser.add_option("", "--headings", action="store", + type="inkbool", dest="headings", default='False', + help="the first line of the CSV file consists of headings for the columns") + + self.OptionParser.add_option("-r", "--rotate", action="store", + type="inkbool", dest="rotate", default='False', + help="Draw barchart horizontally") + + self.OptionParser.add_option("-W", "--bar-width", action="store", + type="int", dest="bar_width", default='10', + help="width of bars") + + self.OptionParser.add_option("-p", "--pie-radius", action="store", + type="int", dest="pie_radius", default='100', + help="radius of pie-charts") + + self.OptionParser.add_option("-H", "--bar-height", action="store", + type="int", dest="bar_height", default='100', + help="height of bars") + + self.OptionParser.add_option("-O", "--bar-offset", action="store", + type="int", dest="bar_offset", default='5', + help="distance between bars") + + self.OptionParser.add_option("", "--stroke-width", action="store", + type="float", dest="stroke_width", default='1') + + self.OptionParser.add_option("-o", "--text-offset", action="store", + type="int", dest="text_offset", default='5', + help="distance between bar and descriptions") + + self.OptionParser.add_option("", "--heading-offset", action="store", + type="int", dest="heading_offset", default='50', + help="distance between chart and chart title") + + self.OptionParser.add_option("", "--segment-overlap", action="store", + type="inkbool", dest="segment_overlap", default='False', + help="work around aliasing effects by letting pie chart segments overlap") + + self.OptionParser.add_option("-F", "--font", action="store", + type="string", dest="font", default='sans-serif', + help="font of description") + + self.OptionParser.add_option("-S", "--font-size", action="store", + type="int", dest="font_size", default='10', + help="font size of description") + + self.OptionParser.add_option("-C", "--font-color", action="store", + type="string", dest="font_color", default='black', + help="font color of description") + #Dummy: + self.OptionParser.add_option("","--input_sections") + + self.OptionParser.add_option("-V", "--show_values", action="store", + type="inkbool", dest="show_values", default='False', + help="Show values in chart") + + def effect(self): + """ + Effect behaviour. + Overrides base class' method and inserts a nice looking chart into SVG document. + """ + # Get script's "--what" option value and process the data type --- i concess the if term is a little bit of magic + what = self.options.what + keys = [] + values = [] + orig_values = [] + keys_present = True + pie_abs = False + cnt = 0 + csv_file_name = self.options.filename + csv_delimiter = self.options.csv_delimiter + input_type = self.options.input_type + col_key = self.options.col_key + col_val = self.options.col_val + show_values = self.options.show_values + encoding = self.options.encoding.strip() or 'utf-8' + headings = self.options.headings + heading_offset = self.options.heading_offset + + if input_type == "\"file\"": + csv_file = open(csv_file_name, "r") + + for linenum, line in enumerate(csv_file): + value = line.decode(encoding).split(csv_delimiter) + #make sure that there is at least one value (someone may want to use it as description) + if len(value) >= 1: + # allow to parse headings as strings + if linenum == 0 and headings: + heading = value[col_val] + else: + keys.append(value[col_key]) + # replace comma decimal separator from file by colon, + # to avoid file editing for people whose programs output + # values with comma + values.append(float(value[col_val].replace(",","."))) + csv_file.close() + + elif input_type == "\"direct_input\"": + what = re.findall("([A-Z|a-z|0-9]+:[0-9]+\.?[0-9]*)", what) + for value in what: + value = value.split(":") + keys.append(value[0]) + values.append(float(value[1])) + + # warn about negative values (not yet supported) + for value in values: + if value < 0: + inkex.errormsg("Negative values are currently not supported!") + return + + # Get script's "--type" option value. + charttype = self.options.type + + if charttype == "pie_abs": + pie_abs = True + charttype = "pie" + + # Get access to main SVG document element and get its dimensions. + svg = self.document.getroot() + + # Get the page attibutes: + width = self.getUnittouu(svg.get('width')) + height = self.getUnittouu(svg.attrib['height']) + + # Create a new layer. + layer = inkex.etree.SubElement(svg, 'g') + layer.set(inkex.addNS('label', 'inkscape'), 'Chart-Layer: %s' % (what)) + layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer') + + # Check if a drop shadow should be drawn: + draw_blur = self.options.blur + + if draw_blur: + # Get defs of Document + defs = self.xpathSingle('/svg:svg//svg:defs') + if defs == None: + defs = inkex.etree.SubElement(self.document.getroot(), inkex.addNS('defs', 'svg')) + + # Create new Filter + filt = inkex.etree.SubElement(defs,inkex.addNS('filter', 'svg')) + filtId = self.uniqueId('filter') + self.filtId = 'filter:url(#%s);' % filtId + for k, v in [('id', filtId), ('height', "3"), + ('width', "3"), + ('x', '-0.5'), ('y', '-0.5')]: + filt.set(k, v) + + # Append Gaussian Blur to that Filter + fe = inkex.etree.SubElement(filt, inkex.addNS('feGaussianBlur', 'svg')) + fe.set('stdDeviation', "1.1") + + # Set Default Colors + self.options.colors_override.strip() + if len(self.options.colors_override) > 0: + colors = self.options.colors_override + else: + colors = self.options.colors + + if colors[0].isalpha(): + colors = get_color_scheme(colors) + else: + colors = re.findall("(#[0-9a-fA-F]{6})", colors) + #to be sure we create a fallback: + if len(colors) == 0: + colors = get_color_scheme() + + color_count = len(colors) + + if self.options.reverse_colors: + colors.reverse() + + # Those values should be self-explanatory: + bar_height = self.options.bar_height + bar_width = self.options.bar_width + bar_offset = self.options.bar_offset + # offset of the description in stacked-bar-charts: + # stacked_bar_text_offset=self.options.stacked_bar_text_offset + text_offset = self.options.text_offset + # prevents ugly aliasing effects between pie chart segments by overlapping + segment_overlap = self.options.segment_overlap + + # get font + font = self.options.font + font_size = self.options.font_size + font_color = self.options.font_color + + # get rotation + rotate = self.options.rotate + + pie_radius = self.options.pie_radius + stroke_width = self.options.stroke_width + + if charttype == "bar": + ######### + ###BAR### + ######### + + # iterate all values, use offset to draw the bars in different places + offset = 0 + color = 0 + + # Normalize the bars to the largest value + try: + value_max = max(values) + except ValueError: + value_max = 0.0 + + for x in range(len(values)): + orig_values.append(values[x]) + values[x] = (values[x]/value_max) * bar_height + + # Draw Single bars with their shadows + for value in values: + + # draw drop shadow, if necessary + if draw_blur: + # Create shadow element + shadow = inkex.etree.Element(inkex.addNS("rect", "svg")) + # Set chart position to center of document. Make it horizontal or vertical + if not rotate: + shadow.set('x', str(width/2 + offset + 1)) + shadow.set('y', str(height/2 - int(value) + 1)) + shadow.set("width", str(bar_width)) + shadow.set("height", str(int(value))) + else: + shadow.set('y', str(width/2 + offset + 1)) + shadow.set('x', str(height/2 + 1)) + shadow.set("height", str(bar_width)) + shadow.set("width", str(int(value))) + + # Set shadow blur (connect to filter object in xml path) + shadow.set("style", "filter:url(#filter)") + + # Create rectangle element + rect = inkex.etree.Element(inkex.addNS('rect', 'svg')) + + # Set chart position to center of document. + if not rotate: + rect.set('x', str(width/2 + offset)) + rect.set('y', str(height/2 - int(value))) + rect.set("width", str(bar_width)) + rect.set("height", str(int(value))) + else: + rect.set('y', str(width/2 + offset)) + rect.set('x', str(height/2)) + rect.set("height", str(bar_width)) + rect.set("width", str(int(value))) + + rect.set("style", "fill:" + colors[color % color_count]) + + # If keys are given, create text elements + if keys_present: + text = inkex.etree.Element(inkex.addNS('text', 'svg')) + if not rotate: #=vertical + text.set("transform", "matrix(0,-1,1,0,0,0)") + #y after rotation: + text.set("x", "-" + str(height/2 + text_offset)) + #x after rotation: + text.set("y", str(width/2 + offset + bar_width/2 + font_size/3)) + else: #=horizontal + text.set("y", str(width/2 + offset + bar_width/2 + font_size/3)) + text.set("x", str(height/2 - text_offset)) + + 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;text-align:end;text-anchor:end;fill:"\ + + font_color) + + text.text = keys[cnt] + + # Increase Offset and Color + #offset=offset+bar_width+bar_offset + color = (color + 1) % 8 + # Connect elements together. + if draw_blur: + layer.append(shadow) + layer.append(rect) + if keys_present: + layer.append(text) + + if show_values: + vtext = inkex.etree.Element(inkex.addNS('text', 'svg')) + if not rotate: #=vertical + vtext.set("transform", "matrix(0,-1,1,0,0,0)") + #y after rotation: + vtext.set("x", "-"+str(height/2+text_offset-value-text_offset-text_offset)) + #x after rotation: + vtext.set("y", str(width/2+offset+bar_width/2+font_size/3)) + else: #=horizontal + vtext.set("y", str(width/2+offset+bar_width/2+font_size/3)) + vtext.set("x", str(height/2-text_offset+value+text_offset+text_offset)) + + vtext.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;text-align:start;text-anchor:start;fill:"\ + + font_color) + + vtext.text = str(int(orig_values[cnt])) + layer.append(vtext) + + cnt = cnt+1 + offset = offset + bar_width + bar_offset + + # set x position for heading line + if not rotate: + heading_x = width/2 # TODO: adjust + else: + heading_x = width/2 # TODO: adjust + + + elif charttype == "pie": + ######### + ###PIE### + ######### + # Iterate all values to draw the different slices + color = 0 + + # Create the shadow first (if it should be created): + if draw_blur: + shadow = inkex.etree.Element(inkex.addNS("circle", "svg")) + shadow.set('cx', str(width/2)) + shadow.set('cy', str(height/2)) + shadow.set('r', str(pie_radius)) + shadow.set("style", "filter:url(#filter);fill:#000000") + layer.append(shadow) + + + # Add a grey background circle with a light stroke + background = inkex.etree.Element(inkex.addNS("circle", "svg")) + background.set("cx", str(width/2)) + background.set("cy", str(height/2)) + background.set("r", str(pie_radius)) + background.set("style", "stroke:#ececec;fill:#f9f9f9") + layer.append(background) + + #create value sum in order to divide the slices + try: + valuesum = sum(values) + + except ValueError: + valuesum = 0 + + if pie_abs: + valuesum = 100 + + num_values = len(values) + + # Set an offsetangle + offset = 0 + + # Draw single slices + for i in range(num_values): + value = values[i] + # Calculate the PI-angles for start and end + angle = (2*3.141592) / valuesum * float(value) + start = offset + end = offset + angle + + # proper overlapping + if segment_overlap: + if i != num_values-1: + end += 0.09 # add a 5° overlap + if i == 0: + start -= 0.09 # let the first element overlap into the other direction + + #then add the slice + pieslice = inkex.etree.Element(inkex.addNS("path", "svg")) + pieslice.set(inkex.addNS('type', 'sodipodi'), 'arc') + pieslice.set(inkex.addNS('cx', 'sodipodi'), str(width/2)) + pieslice.set(inkex.addNS('cy', 'sodipodi'), str(height/2)) + pieslice.set(inkex.addNS('rx', 'sodipodi'), str(pie_radius)) + pieslice.set(inkex.addNS('ry', 'sodipodi'), str(pie_radius)) + pieslice.set(inkex.addNS('start', 'sodipodi'), str(start)) + pieslice.set(inkex.addNS('end', 'sodipodi'), str(end)) + pieslice.set("style", "fill:"+ colors[color % color_count] + ";stroke:none;fill-opacity:1") + + #If text is given, draw short paths and add the text + if keys_present: + path = inkex.etree.Element(inkex.addNS("path", "svg")) + 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))) + + path.set("style", "fill:none;stroke:" + + font_color + ";stroke-width:" + str(stroke_width) + + "px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1") + layer.append(path) + text = inkex.etree.Element(inkex.addNS('text', 'svg')) + text.set("x", str((width/2) + (pie_radius + text_offset) * math.cos(angle/2 + offset))) + text.set("y", str((height/2) + (pie_radius + text_offset) * math.sin(angle/2 + offset) + font_size/3)) + 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 + # check if it is right or left of the Pie + if math.cos(angle/2 + offset) > 0: + text.set("style", textstyle) + else: + text.set("style", textstyle + ";text-align:end;text-anchor:end") + text.text = keys[cnt] + if show_values: + text.text = text.text + "(" + str(values[cnt]) + + if pie_abs: + text.text = text.text + " %" + + text.text = text.text + ")" + + cnt = cnt + 1 + layer.append(text) + + # increase the rotation-offset and the colorcycle-position + offset = offset + angle + color = (color + 1) % 8 + + # append the objects to the extension-layer + layer.append(pieslice) + + # set x position for heading line + heading_x = width/2 - pie_radius # TODO: adjust + + elif charttype == "stbar": + ################# + ###STACKED BAR### + ################# + # Iterate over all values to draw the different slices + color = 0 + + #create value sum in order to divide the bars + try: + valuesum = sum(values) + except ValueError: + valuesum = 0.0 + + for value in values: + valuesum = valuesum + float(value) + + # Init offset + offset = 0 + + if draw_blur: + # Create rectangle element + shadow = inkex.etree.Element(inkex.addNS("rect", "svg")) + # Set chart position to center of document. + if not rotate: + shadow.set('x', str(width/2)) + shadow.set('y', str(height/2 - bar_height/2)) + else: + shadow.set('x', str(width/2)) + shadow.set('y', str(height/2)) + # Set rectangle properties + if not rotate: + shadow.set("width", str(bar_width)) + shadow.set("height", str(bar_height/2)) + else: + shadow.set("width",str(bar_height/2)) + shadow.set("height", str(bar_width)) + # Set shadow blur (connect to filter object in xml path) + shadow.set("style", "filter:url(#filter)") + layer.append(shadow) + + i = 0 + # Draw Single bars + for value in values: + + # Calculate the individual heights normalized on 100units + normedvalue = (bar_height / valuesum) * float(value) + + # Create rectangle element + rect = inkex.etree.Element(inkex.addNS('rect', 'svg')) + + # Set chart position to center of document. + if not rotate: + rect.set('x', str(width / 2 )) + rect.set('y', str(height / 2 - offset - normedvalue)) + else: + rect.set('x', str(width / 2 + offset )) + rect.set('y', str(height / 2 )) + # Set rectangle properties + if not rotate: + rect.set("width", str(bar_width)) + rect.set("height", str(normedvalue)) + else: + rect.set("height", str(bar_width)) + rect.set("width", str(normedvalue)) + rect.set("style", "fill:" + colors[color % color_count]) + + #If text is given, draw short paths and add the text + # TODO: apply overlap workaround for visible gaps in between + if keys_present: + if not rotate: + path = inkex.etree.Element(inkex.addNS("path", "svg")) + path.set("d","m " + str((width + bar_width)/2) + "," + + str(height/2 - offset - (normedvalue / 2)) + " " + + str(bar_width/2 + text_offset) + ",0") + path.set("style", "fill:none;stroke:" + font_color + + ";stroke-width:" + str(stroke_width) + + "px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1") + layer.append(path) + text = inkex.etree.Element(inkex.addNS('text', 'svg')) + text.set("x", str(width/2 + bar_width + text_offset + 1)) + text.set("y", str(height/ 2 - offset + font_size/3 - (normedvalue/2))) + 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) + text.text = keys[cnt] + cnt = cnt + 1 + layer.append(text) + else: + path = inkex.etree.Element(inkex.addNS("path", "svg")) + path.set("d","m " + str((width)/2 + offset + normedvalue/2) + "," + + str(height / 2 + bar_width/2) + " 0," + + str(bar_width/2 + (font_size * i) + text_offset)) #line + path.set("style", "fill:none;stroke:" + font_color + + ";stroke-width:" + str(stroke_width) + + "px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1") + layer.append(path) + text = inkex.etree.Element(inkex.addNS('text', 'svg')) + text.set("x", str((width)/2 + offset + normedvalue/2 - font_size/3)) + text.set("y", str((height/2) + bar_width + (font_size * (i + 1)) + text_offset)) + 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) + text.text = keys[color] + layer.append(text) + + # Increase Offset and Color + offset = offset + normedvalue + color = (color + 1) % 8 + + # Draw rectangle + layer.append(rect) + i += 1 + + # set x position for heading line + if not rotate: + heading_x = width/2 + offset + normedvalue # TODO: adjust + else: + heading_x = width/2 + offset + normedvalue # TODO: adjust + + if headings and input_type == "\"file\"": + headingtext = inkex.etree.Element(inkex.addNS('text', 'svg')) + headingtext.set("y", str(height/2 + heading_offset)) + headingtext.set("x", str(heading_x)) + headingtext.set("style", "font-size:" + str(font_size + 4)\ + + "px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:"\ + + font + ";-inkscape-font-specification:Bitstream Charter;text-align:end;text-anchor:end;fill:"\ + + font_color) + + headingtext.text = heading + layer.append(headingtext) + + def getUnittouu(self, param): + try: + return inkex.unittouu(param) + except AttributeError: + return self.unittouu(param) + +if __name__ == '__main__': + # Create effect instance and apply it. + effect = NiceChart() + effect.affect() |
