[Python-modules-commits] [python-leather] 01/03: New upstream version 0.3.3
Ghislain Vaillant
ghisvail-guest at moszumanska.debian.org
Fri Mar 10 18:12:50 UTC 2017
This is an automated email from the git hooks/post-receive script.
ghisvail-guest pushed a commit to branch master
in repository python-leather.
commit 4d009cfca13002e8a3fd8ecf236308628fff0972
Author: Ghislain Antony Vaillant <ghisvail at gmail.com>
Date: Fri Mar 10 14:31:24 2017 +0000
New upstream version 0.3.3
---
PKG-INFO | 61 ++++++
README.rst | 31 +++
leather.egg-info/PKG-INFO | 61 ++++++
leather.egg-info/SOURCES.txt | 35 +++
leather.egg-info/dependency_links.txt | 1 +
leather.egg-info/requires.txt | 1 +
leather.egg-info/top_level.txt | 1 +
leather/__init__.py | 12 ++
leather/axis.py | 193 +++++++++++++++++
leather/chart.py | 396 ++++++++++++++++++++++++++++++++++
leather/data_types.py | 47 ++++
leather/grid.py | 120 +++++++++++
leather/lattice.py | 172 +++++++++++++++
leather/scales/__init__.py | 6 +
leather/scales/base.py | 140 ++++++++++++
leather/scales/linear.py | 61 ++++++
leather/scales/ordinal.py | 63 ++++++
leather/scales/temporal.py | 76 +++++++
leather/series/__init__.py | 4 +
leather/series/base.py | 138 ++++++++++++
leather/series/category.py | 88 ++++++++
leather/shapes/__init__.py | 7 +
leather/shapes/bars.py | 79 +++++++
leather/shapes/base.py | 101 +++++++++
leather/shapes/columns.py | 78 +++++++
leather/shapes/dots.py | 90 ++++++++
leather/shapes/line.py | 94 ++++++++
leather/svg.py | 43 ++++
leather/testcase.py | 39 ++++
leather/theme.py | 128 +++++++++++
leather/ticks/__init__.py | 5 +
leather/ticks/base.py | 18 ++
leather/ticks/score.py | 174 +++++++++++++++
leather/ticks/score_time.py | 168 +++++++++++++++
leather/utils.py | 188 ++++++++++++++++
setup.cfg | 5 +
setup.py | 48 +++++
37 files changed, 2972 insertions(+)
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..20d0421
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,61 @@
+Metadata-Version: 1.1
+Name: leather
+Version: 0.3.3
+Summary: Python charting for 80% of humans.
+Home-page: http://leather.readthedocs.io/
+Author: Christopher Groskopf
+Author-email: chrisgroskopf at gmail.com
+License: MIT
+Description: .. image:: https://travis-ci.org/wireservice/leather.png
+ :target: https://travis-ci.org/wireservice/leather
+ :alt: Build status
+
+ .. image:: https://img.shields.io/pypi/dw/leather.svg
+ :target: https://pypi.python.org/pypi/leather
+ :alt: PyPI downloads
+
+ .. image:: https://img.shields.io/pypi/v/leather.svg
+ :target: https://pypi.python.org/pypi/leather
+ :alt: Version
+
+ .. image:: https://img.shields.io/pypi/l/leather.svg
+ :target: https://pypi.python.org/pypi/leather
+ :alt: License
+
+ .. image:: https://img.shields.io/pypi/pyversions/leather.svg
+ :target: https://pypi.python.org/pypi/leather
+ :alt: Support Python versions
+
+ Leather is the Python charting library for those who need charts *now* and don't care if they're perfect.
+
+ Leather isn't picky. It's rough. It gets dirty. It looks sexy just hanging on the back of a chair. Leather doesn't need your accessories. Leather is how Snake Plissken would make charts.
+
+ Get it?
+
+ Important links:
+
+ * Documentation: http://leather.rtfd.io
+ * Repository: https://github.com/wireservice/leather
+ * Issues: https://github.com/wireservice/leather/issues
+
+Platform: UNKNOWN
+Classifier: Development Status :: 3 - Alpha
+Classifier: Framework :: IPython
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Topic :: Multimedia :: Graphics
+Classifier: Natural Language :: English
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Scientific/Engineering :: Information Analysis
+Classifier: Topic :: Scientific/Engineering :: Visualization
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..de947cf
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,31 @@
+.. image:: https://travis-ci.org/wireservice/leather.png
+ :target: https://travis-ci.org/wireservice/leather
+ :alt: Build status
+
+.. image:: https://img.shields.io/pypi/dw/leather.svg
+ :target: https://pypi.python.org/pypi/leather
+ :alt: PyPI downloads
+
+.. image:: https://img.shields.io/pypi/v/leather.svg
+ :target: https://pypi.python.org/pypi/leather
+ :alt: Version
+
+.. image:: https://img.shields.io/pypi/l/leather.svg
+ :target: https://pypi.python.org/pypi/leather
+ :alt: License
+
+.. image:: https://img.shields.io/pypi/pyversions/leather.svg
+ :target: https://pypi.python.org/pypi/leather
+ :alt: Support Python versions
+
+Leather is the Python charting library for those who need charts *now* and don't care if they're perfect.
+
+Leather isn't picky. It's rough. It gets dirty. It looks sexy just hanging on the back of a chair. Leather doesn't need your accessories. Leather is how Snake Plissken would make charts.
+
+Get it?
+
+Important links:
+
+* Documentation: http://leather.rtfd.io
+* Repository: https://github.com/wireservice/leather
+* Issues: https://github.com/wireservice/leather/issues
diff --git a/leather.egg-info/PKG-INFO b/leather.egg-info/PKG-INFO
new file mode 100644
index 0000000..20d0421
--- /dev/null
+++ b/leather.egg-info/PKG-INFO
@@ -0,0 +1,61 @@
+Metadata-Version: 1.1
+Name: leather
+Version: 0.3.3
+Summary: Python charting for 80% of humans.
+Home-page: http://leather.readthedocs.io/
+Author: Christopher Groskopf
+Author-email: chrisgroskopf at gmail.com
+License: MIT
+Description: .. image:: https://travis-ci.org/wireservice/leather.png
+ :target: https://travis-ci.org/wireservice/leather
+ :alt: Build status
+
+ .. image:: https://img.shields.io/pypi/dw/leather.svg
+ :target: https://pypi.python.org/pypi/leather
+ :alt: PyPI downloads
+
+ .. image:: https://img.shields.io/pypi/v/leather.svg
+ :target: https://pypi.python.org/pypi/leather
+ :alt: Version
+
+ .. image:: https://img.shields.io/pypi/l/leather.svg
+ :target: https://pypi.python.org/pypi/leather
+ :alt: License
+
+ .. image:: https://img.shields.io/pypi/pyversions/leather.svg
+ :target: https://pypi.python.org/pypi/leather
+ :alt: Support Python versions
+
+ Leather is the Python charting library for those who need charts *now* and don't care if they're perfect.
+
+ Leather isn't picky. It's rough. It gets dirty. It looks sexy just hanging on the back of a chair. Leather doesn't need your accessories. Leather is how Snake Plissken would make charts.
+
+ Get it?
+
+ Important links:
+
+ * Documentation: http://leather.rtfd.io
+ * Repository: https://github.com/wireservice/leather
+ * Issues: https://github.com/wireservice/leather/issues
+
+Platform: UNKNOWN
+Classifier: Development Status :: 3 - Alpha
+Classifier: Framework :: IPython
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Topic :: Multimedia :: Graphics
+Classifier: Natural Language :: English
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Scientific/Engineering :: Information Analysis
+Classifier: Topic :: Scientific/Engineering :: Visualization
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/leather.egg-info/SOURCES.txt b/leather.egg-info/SOURCES.txt
new file mode 100644
index 0000000..11dfca0
--- /dev/null
+++ b/leather.egg-info/SOURCES.txt
@@ -0,0 +1,35 @@
+README.rst
+setup.py
+leather/__init__.py
+leather/axis.py
+leather/chart.py
+leather/data_types.py
+leather/grid.py
+leather/lattice.py
+leather/svg.py
+leather/testcase.py
+leather/theme.py
+leather/utils.py
+leather.egg-info/PKG-INFO
+leather.egg-info/SOURCES.txt
+leather.egg-info/dependency_links.txt
+leather.egg-info/requires.txt
+leather.egg-info/top_level.txt
+leather/scales/__init__.py
+leather/scales/base.py
+leather/scales/linear.py
+leather/scales/ordinal.py
+leather/scales/temporal.py
+leather/series/__init__.py
+leather/series/base.py
+leather/series/category.py
+leather/shapes/__init__.py
+leather/shapes/bars.py
+leather/shapes/base.py
+leather/shapes/columns.py
+leather/shapes/dots.py
+leather/shapes/line.py
+leather/ticks/__init__.py
+leather/ticks/base.py
+leather/ticks/score.py
+leather/ticks/score_time.py
\ No newline at end of file
diff --git a/leather.egg-info/dependency_links.txt b/leather.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/leather.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/leather.egg-info/requires.txt b/leather.egg-info/requires.txt
new file mode 100644
index 0000000..980b4bc
--- /dev/null
+++ b/leather.egg-info/requires.txt
@@ -0,0 +1 @@
+six>=1.6.1
diff --git a/leather.egg-info/top_level.txt b/leather.egg-info/top_level.txt
new file mode 100644
index 0000000..e954dad
--- /dev/null
+++ b/leather.egg-info/top_level.txt
@@ -0,0 +1 @@
+leather
diff --git a/leather/__init__.py b/leather/__init__.py
new file mode 100644
index 0000000..6a9e46e
--- /dev/null
+++ b/leather/__init__.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+from leather.axis import Axis
+from leather.data_types import Number, Text
+from leather.chart import Chart
+from leather.grid import Grid
+from leather.lattice import Lattice
+from leather.scales import Scale, Linear, Ordinal, Temporal
+from leather.series import Series, CategorySeries, key_function
+from leather.shapes import Shape, Bars, Columns, Dots, Line, style_function
+from leather.testcase import LeatherTestCase
+from leather import theme
diff --git a/leather/axis.py b/leather/axis.py
new file mode 100644
index 0000000..5b3aff1
--- /dev/null
+++ b/leather/axis.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python
+
+import xml.etree.ElementTree as ET
+
+import six
+
+from leather import svg
+from leather import theme
+
+
+class Axis(object):
+ """
+ A horizontal or vertical chart axis.
+
+ :param ticks:
+ Instead of inferring tick values from the data, use exactly this
+ sequence of ticks values. These will still be passed to the
+ :code:`tick_formatter`.
+ :param tick_formatter:
+ An optional :func:`.tick_format_function`.
+ """
+ def __init__(self, ticks=None, tick_formatter=None, name=None):
+ self._ticks = ticks
+ self._tick_formatter = tick_formatter
+ self._name = six.text_type(name) if name is not None else None
+
+ def _estimate_left_tick_width(self, scale):
+ """
+ Estimate the y axis space used by tick labels.
+ """
+ tick_values = self._ticks or scale.ticks()
+ tick_count = len(tick_values)
+ tick_formatter = self._tick_formatter or scale.format_tick
+ max_len = 0
+
+ for i, value in enumerate(tick_values):
+ max_len = max(max_len, len(tick_formatter(value, i, tick_count)))
+
+ return max_len * theme.tick_font_char_width
+
+ def estimate_label_margin(self, scale, orient):
+ """
+ Estimate the space needed for the tick labels.
+ """
+ margin = 0
+
+ if orient == 'left':
+ margin += self._estimate_left_tick_width(scale) + (theme.tick_size * 2)
+ elif orient == 'bottom':
+ margin += theme.tick_font_char_height + (theme.tick_size * 2)
+
+ if self._name:
+ margin += theme.axis_title_font_char_height + theme.axis_title_gap
+
+ return margin
+
+ def to_svg(self, width, height, scale, orient):
+ """
+ Render this axis to SVG elements.
+ """
+ group = ET.Element('g')
+ group.set('class', 'axis ' + orient)
+
+ # Axis title
+ if self._name is not None:
+ if orient == 'left':
+ title_x = -(self._estimate_left_tick_width(scale) + theme.axis_title_gap)
+ title_y = height / 2
+ dy=''
+ transform = svg.rotate(270, title_x, title_y)
+ elif orient == 'bottom':
+ title_x = width / 2
+ title_y = height + theme.tick_font_char_height + (theme.tick_size * 2) + theme.axis_title_gap
+ dy='1em'
+ transform = ''
+
+ title = ET.Element('text',
+ x=six.text_type(title_x),
+ y=six.text_type(title_y),
+ dy=dy,
+ fill=theme.axis_title_color,
+ transform=transform
+ )
+ title.set('text-anchor', 'middle')
+ title.set('font-family', theme.axis_title_font_family)
+ title.text = self._name
+
+ group.append(title)
+
+ # Ticks
+ if orient == 'left':
+ label_x = -(theme.tick_size * 2)
+ x1 = -theme.tick_size
+ x2 = width
+ range_min = height
+ range_max = 0
+ elif orient == 'bottom':
+ label_y = height + (theme.tick_size * 2)
+ y1 = 0
+ y2 = height + theme.tick_size
+ range_min = 0
+ range_max = width
+
+ tick_values = self._ticks or scale.ticks()
+ tick_count = len(tick_values)
+ tick_formatter = self._tick_formatter or scale.format_tick
+
+ zero_tick_group = None
+
+ for i, value in enumerate(tick_values):
+ # Tick group
+ tick_group = ET.Element('g')
+ tick_group.set('class', 'tick')
+
+ if value == 0:
+ zero_tick_group = tick_group
+ else:
+ group.append(tick_group)
+
+ # Tick line
+ projected_value = scale.project(value, range_min, range_max)
+
+ if value == 0:
+ tick_color = theme.zero_color
+ else:
+ tick_color = theme.tick_color
+
+ if orient == 'left':
+ y1 = projected_value
+ y2 = projected_value
+
+ elif orient == 'bottom':
+ x1 = projected_value
+ x2 = projected_value
+
+ tick = ET.Element('line',
+ x1=six.text_type(x1),
+ y1=six.text_type(y1),
+ x2=six.text_type(x2),
+ y2=six.text_type(y2),
+ stroke=tick_color
+ )
+ tick.set('stroke-width', six.text_type(theme.tick_width))
+
+ tick_group.append(tick)
+
+ # Tick label
+ if orient == 'left':
+ x = label_x
+ y = projected_value
+ dy = '0.32em'
+ text_anchor = 'end'
+ elif orient == 'bottom':
+ x = projected_value
+ y = label_y
+ dy = '1em'
+ text_anchor = 'middle'
+
+ label = ET.Element('text',
+ x=six.text_type(x),
+ y=six.text_type(y),
+ dy=dy,
+ fill=theme.label_color
+ )
+ label.set('text-anchor', text_anchor)
+ label.set('font-family', theme.tick_font_family)
+
+ value = tick_formatter(value, i, tick_count)
+ label.text = six.text_type(value)
+
+ tick_group.append(label)
+
+ if zero_tick_group is not None:
+ group.append(zero_tick_group)
+
+ return group
+
+
+def tick_format_function(value, index, tick_count):
+ """
+ This example shows how to define a function to format tick values for
+ display.
+
+ :param x:
+ The value to be formatted.
+ :param index:
+ The index of the tick.
+ :param tick_count:
+ The total number of ticks being displayed.
+ :returns:
+ A stringified tick value for display.
+ """
+ return six.text_type(value)
diff --git a/leather/chart.py b/leather/chart.py
new file mode 100644
index 0000000..e272183
--- /dev/null
+++ b/leather/chart.py
@@ -0,0 +1,396 @@
+#!/usr/bin/env python
+
+from copy import copy
+import os
+import xml.etree.ElementTree as ET
+
+import six
+
+from leather.axis import Axis
+from leather.data_types import Date, DateTime
+from leather.scales import Scale, Linear, Temporal
+from leather.series import Series, CategorySeries
+from leather.shapes import Bars, Columns, Dots, Line
+import leather.svg as svg
+from leather import theme
+from leather.utils import X, Y, DIMENSION_NAMES, Box, IPythonSVG, warn
+
+
+class Chart(object):
+ """
+ Container for all chart types.
+
+ :param title:
+ An optional title that will be rendered at the top of the chart.
+ """
+ def __init__(self, title=None):
+ self._title = title
+ self._series_colors = theme.default_series_colors
+
+ self._layers = []
+ self._types = [None, None]
+ self._scales = [None, None]
+ self._axes = [None, None]
+
+ def _palette(self):
+ """
+ Return a generator for series colors.
+ """
+ return (color for color in self._series_colors)
+
+ def set_x_scale(self, scale):
+ """
+ Set the X :class:`.Scale` for this chart.
+ """
+ self._scales[X] = scale
+
+ def set_y_scale(self, scale):
+ """
+ See :meth:`.Chart.set_x_scale`.
+ """
+ self._scales[Y] = scale
+
+ def add_x_scale(self, domain_min, domain_max):
+ """
+ Create and add a :class:`.Scale`.
+
+ If the provided domain values are :class:`date` or :class:`datetime`
+ then a :class:`.Temporal` scale will be created, otherwise it will
+ :class:`.Linear`.
+
+ If you want to set a custom scale class use :meth:`.Chart.set_x_scale`
+ instead.
+ """
+ scale_type = Linear
+
+ if isinstance(domain_min, Date.types) or isinstance(domain_min, DateTime.types):
+ scale_type = Temporal
+
+ self.set_x_scale(scale_type(domain_min, domain_max))
+
+ def add_y_scale(self, domain_min, domain_max):
+ """
+ See :meth:`.Chart.add_x_scale`.
+ """
+ scale_type = Linear
+
+ if isinstance(domain_min, Date.types) or isinstance(domain_min, DateTime.types):
+ scale_type = Temporal
+
+ self.set_y_scale(scale_type(domain_min, domain_max))
+
+ def set_x_axis(self, axis):
+ """
+ Set an :class:`.Axis` class for this chart.
+ """
+ self._axes[X] = axis
+
+ def set_y_axis(self, axis):
+ """
+ See :meth:`.Chart.set_x_axis`.
+ """
+ self._axes[Y] = axis
+
+ def add_x_axis(self, ticks=None, tick_formatter=None, name=None):
+ """
+ Create and add an X :class:`.Axis`.
+
+ If you want to set a custom axis class use :meth:`.Chart.set_x_axis`
+ instead.
+ """
+ self._axes[X] = Axis(ticks, tick_formatter, name)
+
+ def add_y_axis(self, ticks=None, tick_formatter=None, name=None):
+ """
+ See :meth:`.Chart.add_x_axis`.
+ """
+ self._axes[Y] = Axis(ticks, tick_formatter, name)
+
+ def add_series(self, series, shape):
+ """
+ Add a data :class:`.Series` to the chart. The data types of the new
+ series must be consistent with any series that have already been added.
+
+ There are several shortcuts for adding different types of data series.
+ See :meth:`.Chart.add_bars`, :meth:`.Chart.add_columns`,
+ :meth:`.Chart.add_dots`, and :meth:`.Chart.add_line`.
+ """
+ if self._layers and isinstance(self._layers[0][0], CategorySeries):
+ raise RuntimeError('Additional series can not be added to a chart with a CategorySeries.')
+
+ if isinstance(series, CategorySeries):
+ self._types = series._types
+ else:
+ for dim in [X, Y]:
+ if not self._types[dim]:
+ self._types[dim] = series._types[dim]
+ elif series._types[dim] is not self._types[dim]:
+ raise TypeError('Can\'t mix axis-data types: %s and %s' % (series._types[dim], self._types[dim]))
+
+ shape.validate_series(series)
+
+ self._layers.append((
+ series,
+ shape
+ ))
+
+ def add_bars(self, data, x=None, y=None, name=None, fill_color=None):
+ """
+ Create and add a :class:`.Series` rendered with :class:`.Bars`.
+
+ Note that when creating bars in this way the order of the series data
+ will be reversed so that the first item in the series is displayed
+ as the top-most bar in the graphic. If you don't want this to happen
+ use :meth:`.Chart.add_series` instead.
+ """
+ self.add_series(
+ Series(list(reversed(data)), x=x, y=y, name=name),
+ Bars(fill_color)
+ )
+
+ def add_columns(self, data, x=None, y=None, name=None, fill_color=None):
+ """
+ Create and add a :class:`.Series` rendered with :class:`.Columns`.
+ """
+ self.add_series(
+ Series(data, x=x, y=y, name=name),
+ Columns(fill_color)
+ )
+
+ def add_dots(self, data, x=None, y=None, name=None, fill_color=None, radius=None):
+ """
+ Create and add a :class:`.Series` rendered with :class:`.Dots`.
+ """
+ self.add_series(
+ Series(data, x=x, y=y, name=name),
+ Dots(fill_color, radius)
+ )
+
+ def add_line(self, data, x=None, y=None, name=None, stroke_color=None, width=None):
+ """
+ Create and add a :class:`.Series` rendered with :class:`.Line`.
+ """
+ self.add_series(
+ Series(data, x=x, y=y, name=name),
+ Line(stroke_color, width)
+ )
+
+ def _validate_dimension(self, dimension):
+ """
+ Validates that the given scale and axis are valid for the data that
+ has been added to this chart. If a scale or axis has not been set,
+ generates automated ones.
+ """
+ scale = self._scales[dimension]
+ axis = self._axes[dimension]
+
+ if not scale:
+ scale = Scale.infer(self._layers, dimension, self._types[dimension])
+ else:
+ for series, shape in self._layers:
+ if not scale.contains(series.min(dimension)) or not scale.contains(series.max(dimension)):
+ d = DIMENSION_NAMES[dimension]
+ warn('Data contains values outside %s scale domain. All data points may not be visible on the chart.' % d)
+
+ # Only display once per axis
+ break
+
+ if not axis:
+ axis = Axis()
+
+ return (scale, axis)
+
+ def to_svg_group(self, width=None, height=None):
+ """
+ Render this chart to an SVG group element.
+
+ This can then be placed inside an :code:`<svg>` tag to make a complete
+ SVG graphic.
+
+ See :meth:`.Chart.to_svg` for arguments.
+ """
+ width = width or theme.default_width
+ height = height or theme.default_height
+
+ if not self._layers:
+ raise ValueError('You must add at least one series to the chart before rendering.')
+
+ if isinstance(theme.margin, float):
+ default_margin = width * theme.margin
+
+ margin = Box(
+ top=default_margin,
+ right=default_margin,
+ bottom=default_margin,
+ left=default_margin
+ )
+ elif isinstance(margin, int):
+ margin = Box(margin, margin, margin, margin)
+ elif not isinstance(margin, Box):
+ margin = Box(*margin)
+
+ # Root / background
+ root_group = ET.Element('g')
+
+ root_group.append(ET.Element('rect',
+ x=six.text_type(0),
+ y=six.text_type(0),
+ width=six.text_type(width),
+ height=six.text_type(height),
+ fill=theme.background_color
+ ))
+
+ # Margins
+ margin_group = ET.Element('g')
+ margin_group.set('transform', svg.translate(margin.left, margin.top))
+
+ margin_width = width - (margin.left + margin.right)
+ margin_height = height - (margin.top + margin.bottom)
+
+ root_group.append(margin_group)
+
+ # Header
+ header_group = ET.Element('g')
+
+ header_margin = 0
+
+ if self._title:
+ label = ET.Element('text',
+ x=six.text_type(0),
+ y=six.text_type(0),
+ fill=theme.title_color
+ )
+ label.set('font-family', theme.title_font_family)
+ label.set('font-size', six.text_type(theme.title_font_size))
+ label.text = six.text_type(self._title)
+
+ header_group.append(label)
+ header_margin += theme.title_font_char_height + theme.title_gap
+
+ # Legend
+ if len(self._layers) > 1 or isinstance(self._layers[0][0], CategorySeries):
+ legend_group = ET.Element('g')
+ legend_group.set('transform', svg.translate(0, header_margin))
+
+ indent = 0
+ rows = 1
+ palette = self._palette()
+
+ for series, shape in self._layers:
+ for item_group, item_width in shape.legend_to_svg(series, palette):
+ if indent + item_width > width:
+ indent = 0
+ rows += 1
+
+ y = (rows - 1) * (theme.legend_font_char_height + theme.legend_gap)
+ item_group.set('transform', svg.translate(indent, y))
+
+ indent += item_width
+
+ legend_group.append(item_group)
+
+ legend_height = rows * (theme.legend_font_char_height + theme.legend_gap)
+
+ header_margin += legend_height
+ header_group.append(legend_group)
+
+ margin_group.append(header_group)
+
+ # Body
+ body_group = ET.Element('g')
+ body_group.set('transform', svg.translate(0, header_margin))
+
+ body_width = margin_width
+ body_height = margin_height - header_margin
+
+ margin_group.append(body_group)
+
+ # Axes
+ x_scale, x_axis = self._validate_dimension(X)
+ y_scale, y_axis = self._validate_dimension(Y)
+
+ bottom_margin = x_axis.estimate_label_margin(x_scale, 'bottom')
+ left_margin = y_axis.estimate_label_margin(y_scale, 'left')
+
+ canvas_width = body_width - left_margin
+ canvas_height = body_height - bottom_margin
+
+ axes_group = ET.Element('g')
+ axes_group.set('transform', svg.translate(left_margin, 0))
+
+ axes_group.append(x_axis.to_svg(canvas_width, canvas_height, x_scale, 'bottom'))
+ axes_group.append(y_axis.to_svg(canvas_width, canvas_height, y_scale, 'left'))
+
+ header_group.set('transform', svg.translate(left_margin, 0))
+
+ body_group.append(axes_group)
+
+ # Series
+ series_group = ET.Element('g')
+
+ palette = self._palette()
+
+ for series, shape in self._layers:
+ series_group.append(shape.to_svg(canvas_width, canvas_height, x_scale, y_scale, series, palette))
+
+ axes_group.append(series_group)
+
+ return root_group
+
+ def to_svg(self, path=None, width=None, height=None):
+ """
+ Render this chart to an SVG document.
+
+ The :code:`width` and :code:`height` are specified in SVG's
+ "unitless" units, however, it is usually convenient to specify them
+ as though they were pixels.
+
+ :param path:
+ Filepath or file-like object to write to. If omitted then the SVG
+ will be returned as a string. If running within IPython, then this
+ will return a SVG object to be displayed.
+ :param width:
+ The output width, in SVG user units. Defaults to
+ :data:`.theme.default_chart_width`.
+ :param height:
+ The output height, in SVG user units. Defaults to
+ :data:`.theme.default_chart_height`.
+ """
+ width = width or theme.default_chart_width
+ height = height or theme.default_chart_height
+
+ root = ET.Element('svg',
+ width=six.text_type(width),
+ height=six.text_type(height),
+ version='1.1',
+ xmlns='http://www.w3.org/2000/svg'
+ )
+
+ group = self.to_svg_group(width, height)
+ root.append(group)
+
+ svg_text = svg.stringify(root)
+ close = True
+
+ if path:
+ f = None
+
+ try:
+ if hasattr(path, 'write'):
+ f = path
+ close = False
+ else:
+ dirpath = os.path.dirname(path)
+
+ if dirpath and not os.path.exists(dirpath):
+ os.makedirs(dirpath)
+
+ f = open(path, 'w')
+
+ f.write(svg.HEADER)
+ f.write(svg_text)
+ finally:
+ if close and f is not None:
+ f.close()
+ else:
+ return IPythonSVG(svg_text)
diff --git a/leather/data_types.py b/leather/data_types.py
new file mode 100644
index 0000000..f5e0b0d
--- /dev/null
+++ b/leather/data_types.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+from datetime import date, datetime
+from decimal import Decimal
+
+import six
+
+
+class DataType(object):
+ """
+ Base class for :class:`.Series` data types.
+ """
+ @classmethod
+ def infer(cls, v):
+ for t in [DateTime, Date, Number, Text]:
+ if isinstance(v, t.types):
+ return t
+
+ raise TypeError('No data type available for %s' % type(v))
+
+
+class Date(DataType):
+ """
+ Data representing dates.
+ """
+ types = (date,)
+
+
+class DateTime(DataType):
+ """
+ Data representing dates with times.
+ """
+ types = (datetime,)
+
+
+class Number(DataType):
+ """
+ Data representing numbers.
+ """
+ types = (int, float, Decimal)
+
+
+class Text(DataType):
+ """
+ Data representing text/strings.
+ """
+ types = six.string_types
diff --git a/leather/grid.py b/leather/grid.py
new file mode 100644
index 0000000..9693682
--- /dev/null
+++ b/leather/grid.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+
+import math
+import os
+import xml.etree.ElementTree as ET
+
+import six
+
+import leather.svg as svg
+from leather import theme
+from leather.utils import IPythonSVG
+
+
+class Grid(object):
+ """
+ A container for a set of :class:`.Chart` instances that are rendered in a
+ grid layout.
+ """
+ def __init__(self):
+ self._charts = []
+
+ def add_one(self, chart):
+ """
+ Add a :class:`.Chart` to the grid.
+ """
+ self._charts.append(chart)
+
+ def add_many(self, charts):
+ """
+ Add a sequence of charts to this grid.
+ """
+ self._charts.extend(charts)
+
+ def to_svg(self, path=None, width=None, height=None):
+ """
+ Render the grid to an SVG.
+
+ The :code:`width` and :code:`height` arguments refer to the size of the
+ entire grid. The size of individual charts will be inferred
+ automatically.
+
+ See :meth:`.Chart.to_svg` for arguments.
+ """
... 2240 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-leather.git
More information about the Python-modules-commits
mailing list