Browse Source

Document pygal.*

pull/242/head
Florian Mounier 10 years ago
parent
commit
6f59ff14a9
  1. 21
      pygal/__init__.py
  2. 19
      pygal/_compat.py
  3. 5
      pygal/adapters.py
  4. 9
      pygal/colors.py
  5. 24
      pygal/config.py
  6. 3
      pygal/etree.py
  7. 1
      pygal/interpolate.py
  8. 11
      pygal/serie.py
  9. 12
      pygal/state.py
  10. 23
      pygal/style.py
  11. 14
      pygal/svg.py
  12. 19
      pygal/table.py
  13. 18
      pygal/util.py
  14. 68
      pygal/view.py

21
pygal/__init__.py

@ -17,7 +17,7 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """
Main pygal package Main pygal package.
This package holds all available charts in pygal, the Config class This package holds all available charts in pygal, the Config class
and the maps extensions namespace module. and the maps extensions namespace module.
@ -76,16 +76,17 @@ CHARTS = list(CHARTS_BY_NAME.values())
class PluginImportFixer(object): class PluginImportFixer(object):
"""Allow external map plugins to be imported from pygal.maps package.
It is a ``sys.meta_path`` loader.
""" """
Allow external map plugins to be imported from pygal.maps package.
def __init__(self): It is a ``sys.meta_path`` loader.
pass """
def find_module(self, fullname, path=None): def find_module(self, fullname, path=None):
"""This method says if the module to load can be loaded by """
the load_module function, that is here if it is a ``pygal.maps.*`` Tell if the module to load can be loaded by
the load_module function, ie: if it is a ``pygal.maps.*``
module. module.
""" """
if fullname.startswith('pygal.maps.') and hasattr( if fullname.startswith('pygal.maps.') and hasattr(
@ -94,8 +95,10 @@ class PluginImportFixer(object):
return None return None
def load_module(self, name): def load_module(self, name):
"""Loads the ``pygal.maps.name`` module from """
the previously loaded plugin""" Load the ``pygal.maps.name`` module from the previously
loaded plugin
"""
if name not in sys.modules: if name not in sys.modules:
sys.modules[name] = getattr(maps, name.split('.')[2]) sys.modules[name] = getattr(maps, name.split('.')[2])
return sys.modules[name] return sys.modules[name]

19
pygal/_compat.py

@ -16,9 +16,7 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """Various hacks for transparent python 2 / python 3 support"""
Various hacks for transparent python 2 / python 3 support
"""
import sys import sys
from collections import Iterable from collections import Iterable
@ -34,32 +32,38 @@ else:
def is_list_like(value): def is_list_like(value):
"""Return whether value is an iterable but not a mapping / string"""
return isinstance(value, Iterable) and not isinstance(value, (base, dict)) return isinstance(value, Iterable) and not isinstance(value, (base, dict))
def is_str(string): def is_str(string):
"""Return whether value is a string or a byte list"""
return isinstance(string, base) return isinstance(string, base)
def to_str(string): def to_str(obj):
if not is_str(string): """Cast obj to unicode string"""
return coerce(string) if not is_str(obj):
return string return coerce(obj)
return obj
def to_unicode(string): def to_unicode(string):
"""Force stirng to be a string in python 3 or a unicode in python 2"""
if not isinstance(string, coerce): if not isinstance(string, coerce):
return string.decode('utf-8') return string.decode('utf-8')
return string return string
def u(s): def u(s):
"""Emulate u'str' in python 2, do nothing in python 3"""
if sys.version_info[0] == 2: if sys.version_info[0] == 2:
return s.decode('utf-8') return s.decode('utf-8')
return s return s
def total_seconds(td): def total_seconds(td):
"""Backport of `timedelta.total_second` function for python 2.6"""
if sys.version_info[:2] == (2, 6): if sys.version_info[:2] == (2, 6):
return ( return (
(td.days * 86400 + td.seconds) * 10 ** 6 + td.microseconds (td.days * 86400 + td.seconds) * 10 ** 6 + td.microseconds
@ -68,6 +72,7 @@ def total_seconds(td):
def timestamp(x): def timestamp(x):
"""Get a timestamp from a date in python 3 and python 2"""
if hasattr(x, 'timestamp'): if hasattr(x, 'timestamp'):
from datetime import timezone from datetime import timezone
if x.tzinfo is None: if x.tzinfo is None:

5
pygal/adapters.py

@ -16,10 +16,7 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """Value adapters to use when a chart doesn't accept all value types"""
Value adapters to use when a chart doesn't accept all value types
"""
from decimal import Decimal from decimal import Decimal

9
pygal/colors.py

@ -119,9 +119,10 @@ def parse_color(color):
def unparse_color(r, g, b, a, type): def unparse_color(r, g, b, a, type):
"""Take the r, g, b, a color values and give back """
a type css color string. This is the inverse function of parse_color""" Take the r, g, b, a color values and give back
a type css color string. This is the inverse function of parse_color
"""
if type == '#rgb': if type == '#rgb':
# Don't lose precision on rgb shortcut # Don't lose precision on rgb shortcut
if r % 17 == 0 and g % 17 == 0 and b % 17 == 0: if r % 17 == 0 and g % 17 == 0 and b % 17 == 0:
@ -168,7 +169,7 @@ def adjust(color, attribute, percent):
def rotate(color, percent): def rotate(color, percent):
"""Rotates a color by changing its hue value by percent""" """Rotate a color by changing its hue value by percent"""
return adjust(color, 0, percent) return adjust(color, 0, percent)

24
pygal/config.py

@ -16,10 +16,8 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""Config module holding all options and their default values."""
"""
Config module holding all options and their default values.
"""
from copy import deepcopy from copy import deepcopy
from pygal.style import Style, DefaultStyle from pygal.style import Style, DefaultStyle
from pygal.interpolate import INTERPOLATIONS from pygal.interpolate import INTERPOLATIONS
@ -29,6 +27,7 @@ CONFIG_ITEMS = []
class Key(object): class Key(object):
""" """
Represents a config parameter. Represents a config parameter.
@ -45,7 +44,7 @@ class Key(object):
def __init__( def __init__(
self, default_value, type_, category, doc, self, default_value, type_, category, doc,
subdoc="", subtype=None): subdoc="", subtype=None):
"""Create a configuration key"""
self.value = default_value self.value = default_value
self.type = type_ self.type = type_
self.doc = doc self.doc = doc
@ -59,6 +58,10 @@ class Key(object):
CONFIG_ITEMS.append(self) CONFIG_ITEMS.append(self)
def __repr__(self): def __repr__(self):
"""
Make a documentation repr.
This is a hack to generate doc from inner doc
"""
return """ return """
Type: %s%s      Type: %s%s     
Default: %r      Default: %r     
@ -120,10 +123,11 @@ class Key(object):
class MetaConfig(type): class MetaConfig(type):
"""
Metaclass for configs. Used to get the key name and set it on the value. """Config metaclass. Used to get the key name and set it on the value."""
"""
def __new__(mcs, classname, bases, classdict): def __new__(mcs, classname, bases, classdict):
"""Get the name of the key and set it on the key"""
for k, v in classdict.items(): for k, v in classdict.items():
if isinstance(v, Key): if isinstance(v, Key):
v.name = k v.name = k
@ -132,6 +136,7 @@ class MetaConfig(type):
class BaseConfig(MetaConfig('ConfigBase', (object,), {})): class BaseConfig(MetaConfig('ConfigBase', (object,), {})):
""" """
This class holds the common method for configs. This class holds the common method for configs.
@ -159,7 +164,7 @@ class BaseConfig(MetaConfig('ConfigBase', (object,), {})):
self._update(kwargs) self._update(kwargs)
def _update(self, kwargs): def _update(self, kwargs):
"""Updates the config with the given dictionary""" """Update the config with the given dictionary"""
self.__dict__.update( self.__dict__.update(
dict([(k, v) for (k, v) in kwargs.items() dict([(k, v) for (k, v) in kwargs.items()
if not k.startswith('_') and k in dir(self)])) if not k.startswith('_') and k in dir(self)]))
@ -182,6 +187,7 @@ class BaseConfig(MetaConfig('ConfigBase', (object,), {})):
class CommonConfig(BaseConfig): class CommonConfig(BaseConfig):
"""Class holding options used in both chart and serie configuration""" """Class holding options used in both chart and serie configuration"""
stroke = Key( stroke = Key(
@ -213,6 +219,7 @@ class CommonConfig(BaseConfig):
class Config(CommonConfig): class Config(CommonConfig):
"""Class holding config values""" """Class holding config values"""
style = Key( style = Key(
@ -496,6 +503,7 @@ class Config(CommonConfig):
class SerieConfig(CommonConfig): class SerieConfig(CommonConfig):
"""Class holding serie config values""" """Class holding serie config values"""
secondary = Key( secondary = Key(

3
pygal/etree.py

@ -25,9 +25,11 @@ import os
class Etree(object): class Etree(object):
"""Etree wrapper using lxml.etree or standard xml.etree""" """Etree wrapper using lxml.etree or standard xml.etree"""
def __init__(self): def __init__(self):
"""Create the wrapper"""
from xml.etree import ElementTree as _py_etree from xml.etree import ElementTree as _py_etree
self._py_etree = _py_etree self._py_etree = _py_etree
try: try:
@ -43,6 +45,7 @@ class Etree(object):
self.lxml = self._etree is self._lxml_etree self.lxml = self._etree is self._lxml_etree
def __getattribute__(self, attr): def __getattribute__(self, attr):
"""Retrieve attr from current active etree implementation"""
if (attr not in object.__getattribute__(self, '__dict__') and if (attr not in object.__getattribute__(self, '__dict__') and
attr not in Etree.__dict__): attr not in Etree.__dict__):
return object.__getattribute__(self._etree, attr) return object.__getattribute__(self._etree, attr)

1
pygal/interpolate.py

@ -33,7 +33,6 @@ def quadratic_interpolate(x, y, precision=250, **kwargs):
Interpolate x, y using a quadratic algorithm Interpolate x, y using a quadratic algorithm
https://en.wikipedia.org/wiki/Spline_(mathematics) https://en.wikipedia.org/wiki/Spline_(mathematics)
""" """
n = len(x) - 1 n = len(x) - 1
delta_x = [x2 - x1 for x1, x2 in zip(x, x[1:])] delta_x = [x2 - x1 for x1, x2 in zip(x, x[1:])]
delta_y = [y2 - y1 for y1, y2 in zip(y, y[1:])] delta_y = [y2 - y1 for y1, y2 in zip(y, y[1:])]

11
pygal/serie.py

@ -16,16 +16,16 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """Serie property holder"""
Little helpers for series
"""
from pygal.util import cached_property from pygal.util import cached_property
class Serie(object): class Serie(object):
"""Serie containing title, values and the graph serie index"""
"""Serie class containing title, values and the graph serie index"""
def __init__(self, index, title, values, config, metadata=None): def __init__(self, index, title, values, config, metadata=None):
"""Create the serie with its options"""
self.index = index self.index = index
self.title = title self.title = title
self.values = values self.values = values
@ -35,4 +35,5 @@ class Serie(object):
@cached_property @cached_property
def safe_values(self): def safe_values(self):
"""Property containing all values that are not None"""
return list(filter(lambda x: x is not None, self.values)) return list(filter(lambda x: x is not None, self.values))

12
pygal/state.py

@ -16,14 +16,20 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Class holding state during render
""" """Class holding state during render"""
class State(object): class State(object):
"""
Class containing config values
overriden by chart values
overriden by keyword args
"""
def __init__(self, graph, **kwargs): def __init__(self, graph, **kwargs):
"""Create the transient state"""
self.__dict__.update(**graph.config.__dict__) self.__dict__.update(**graph.config.__dict__)
self.__dict__.update(**graph.__dict__) self.__dict__.update(**graph.__dict__)
self.__dict__.update(**kwargs) self.__dict__.update(**kwargs)

23
pygal/style.py

@ -16,9 +16,8 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """Charts styling classes"""
Charts styling
"""
from __future__ import division from __future__ import division
from pygal.util import cycle_fill from pygal.util import cycle_fill
from pygal import colors from pygal import colors
@ -27,7 +26,9 @@ import sys
class Style(object): class Style(object):
"""Styling class containing colors for the css generation""" """Styling class containing colors for the css generation"""
def __init__( def __init__(
self, self,
background='black', background='black',
@ -45,6 +46,7 @@ class Style(object):
'#899ca1', '#f8f8f2', '#bf4646', '#516083', '#f92672', '#899ca1', '#f8f8f2', '#bf4646', '#516083', '#f92672',
'#82b414', '#fd971f', '#56c2d6', '#808384', '#8c54fe', '#82b414', '#fd971f', '#56c2d6', '#808384', '#8c54fe',
'#465457')): '#465457')):
"""Create the style"""
self.background = background self.background = background
self.plot_background = plot_background self.plot_background = plot_background
self.foreground = foreground self.foreground = foreground
@ -58,7 +60,6 @@ class Style(object):
def get_colors(self, prefix, len_): def get_colors(self, prefix, len_):
"""Get the css color list""" """Get the css color list"""
def color(tupl): def color(tupl):
"""Make a color css""" """Make a color css"""
return (( return ((
@ -75,6 +76,7 @@ class Style(object):
return '\n'.join(map(color, enumerate(colors))) return '\n'.join(map(color, enumerate(colors)))
def to_dict(self): def to_dict(self):
"""Convert instance to a serializable mapping."""
config = {} config = {}
for attr in dir(self): for attr in dir(self):
if not attr.startswith('__'): if not attr.startswith('__'):
@ -296,10 +298,23 @@ for op in ('lighten', 'darken', 'saturate', 'desaturate', 'rotate'):
name = op.capitalize() + 'Style' name = op.capitalize() + 'Style'
def get_style_for(op_name): def get_style_for(op_name):
"""
Return a callable that returns a Style instance
for the given operation
"""
operation = getattr(colors, op_name) operation = getattr(colors, op_name)
def parametric_style(color, step=10, max_=None, base_style=None, def parametric_style(color, step=10, max_=None, base_style=None,
**kwargs): **kwargs):
"""
Generate a parametric Style instance of the parametric operation
This takes several parameters:
* a `step` which correspond on how many colors will be needed
* a `max_` which defines the maximum amplitude of the color effect
* a `base_style` which will be taken as default for everything
except colors
* any keyword arguments setting other style parameters
"""
if max_ is None: if max_ is None:
violency = { violency = {
'darken': 50, 'darken': 50,

14
pygal/svg.py

@ -16,10 +16,8 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Svg helper
""" """Svg helper"""
from __future__ import division from __future__ import division
from pygal._compat import to_str, u from pygal._compat import to_str, u
@ -35,11 +33,14 @@ from pygal import __version__
class Svg(object): class Svg(object):
"""Svg object"""
"""Svg related methods"""
ns = 'http://www.w3.org/2000/svg' ns = 'http://www.w3.org/2000/svg'
xlink_ns = 'http://www.w3.org/1999/xlink' xlink_ns = 'http://www.w3.org/1999/xlink'
def __init__(self, graph): def __init__(self, graph):
"""Create the svg helper with the chart instance"""
self.graph = graph self.graph = graph
if not graph.no_prefix: if not graph.no_prefix:
self.id = '#chart-%s ' % graph.uuid self.id = '#chart-%s ' % graph.uuid
@ -98,7 +99,9 @@ class Svg(object):
os.path.dirname(__file__), 'css', css) os.path.dirname(__file__), 'css', css)
class FontSizes(object): class FontSizes(object):
"""Container for font sizes""" """Container for font sizes"""
fs = FontSizes() fs = FontSizes()
for name in dir(self.graph.state): for name in dir(self.graph.state):
if name.endswith('_font_size'): if name.endswith('_font_size'):
@ -286,6 +289,7 @@ class Svg(object):
self.root.set('height', str(self.graph.height)) self.root.set('height', str(self.graph.height))
def draw_no_data(self): def draw_no_data(self):
"""Write the no data text to the svg"""
no_data = self.node(self.graph.nodes['text_overlay'], 'text', no_data = self.node(self.graph.nodes['text_overlay'], 'text',
x=self.graph.view.width / 2, x=self.graph.view.width / 2,
y=self.graph.view.height / 2, y=self.graph.view.height / 2,
@ -314,7 +318,9 @@ class Svg(object):
return svg return svg
def get_strokes(self): def get_strokes(self):
"""Return a css snippet containing all stroke style options"""
def stroke_dict_to_css(stroke, i=None): def stroke_dict_to_css(stroke, i=None):
"""Return a css style for the given option"""
css = ['%s.series%s {\n' % ( css = ['%s.series%s {\n' % (
self.id, '.serie-%d' % i if i is not None else '')] self.id, '.serie-%d' % i if i is not None else '')]
for key in ('width', 'linejoin', 'linecap', for key in ('width', 'linejoin', 'linecap',

19
pygal/table.py

@ -17,8 +17,9 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """
Table maker HTML Table maker.
This class is used to render an html table from a chart data.
""" """
from pygal.util import template from pygal.util import template
@ -27,18 +28,32 @@ import uuid
class HTML(object): class HTML(object):
"""Lower case adapter of lxml builder"""
def __getattribute__(self, attr): def __getattribute__(self, attr):
"""Get the uppercase builder attribute"""
return getattr(builder, attr.upper()) return getattr(builder, attr.upper())
class Table(object): class Table(object):
"""Table generator class"""
_dual = None _dual = None
def __init__(self, chart): def __init__(self, chart):
"Init the table" """Init the table"""
self.chart = chart self.chart = chart
def render(self, total=False, transpose=False, style=False): def render(self, total=False, transpose=False, style=False):
"""Render the HTMTL table of the chart.
`total` can be specified to include data sums
`transpose` make labels becomes columns
`style` include scoped style for the table
"""
self.chart.setup() self.chart.setup()
ln = self.chart._len ln = self.chart._len
fmt = self.chart._format fmt = self.chart._format

18
pygal/util.py

@ -16,10 +16,9 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Various utils
""" """Various utility functions"""
from __future__ import division from __future__ import division
from pygal._compat import u, is_list_like, to_unicode from pygal._compat import u, is_list_like, to_unicode
import re import re
@ -250,6 +249,7 @@ def decorate(svg, node, metadata):
def alter(node, metadata): def alter(node, metadata):
"""Override nodes attributes from metadata node mapping"""
if node is not None and metadata and 'node' in metadata: if node is not None and metadata and 'node' in metadata:
node.attrib.update( node.attrib.update(
dict((k, str(v)) for k, v in metadata['node'].items())) dict((k, str(v)) for k, v in metadata['node'].items()))
@ -273,14 +273,21 @@ def truncate(string, index):
# # Stolen partly from brownie http://packages.python.org/Brownie/ # # Stolen partly from brownie http://packages.python.org/Brownie/
class cached_property(object): class cached_property(object):
"""Optimize a static property"""
"""Memoize a property"""
def __init__(self, getter, doc=None): def __init__(self, getter, doc=None):
"""Initialize the decorator"""
self.getter = getter self.getter = getter
self.__module__ = getter.__module__ self.__module__ = getter.__module__
self.__name__ = getter.__name__ self.__name__ = getter.__name__
self.__doc__ = doc or getter.__doc__ self.__doc__ = doc or getter.__doc__
def __get__(self, obj, type_=None): def __get__(self, obj, type_=None):
"""
Get descriptor calling the property function and replacing it with
its value or on state if we are in the transient state.
"""
if obj is None: if obj is None:
return self return self
value = self.getter(obj) value = self.getter(obj)
@ -294,6 +301,7 @@ css_comments = re.compile(r'/\*.*?\*/', re.MULTILINE | re.DOTALL)
def minify_css(css): def minify_css(css):
"""Little css minifier"""
# Inspired by slimmer by Peter Bengtsson # Inspired by slimmer by Peter Bengtsson
remove_next_comment = 1 remove_next_comment = 1
for css_comment in css_comments.findall(css): for css_comment in css_comments.findall(css):
@ -328,12 +336,14 @@ def compose(f, g):
def safe_enumerate(iterable): def safe_enumerate(iterable):
"""Enumerate which does not yield None values"""
for i, v in enumerate(iterable): for i, v in enumerate(iterable):
if v is not None: if v is not None:
yield i, v yield i, v
def split_title(title, width, title_fs): def split_title(title, width, title_fs):
"""Split a string for a specified width and font size"""
titles = [] titles = []
if not title: if not title:
return titles return titles

68
pygal/view.py

@ -16,17 +16,19 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Projection and bounding helpers """Projection and bounding helpers"""
"""
from __future__ import division from __future__ import division
from math import sin, cos, log10, pi from math import sin, cos, log10, pi
class Margin(object): class Margin(object):
"""Graph margin"""
"""Class reprensenting a margin (top, right, left, bottom)"""
def __init__(self, top, right, bottom, left): def __init__(self, top, right, bottom, left):
"""Create the margin object from the top, right, left, bottom margin"""
self.top = top self.top = top
self.right = right self.right = right
self.bottom = bottom self.bottom = bottom
@ -44,16 +46,23 @@ class Margin(object):
class Box(object): class Box(object):
"""Chart boundings""" """Chart boundings"""
margin = .02 margin = .02
def __init__(self, xmin=0, ymin=0, xmax=1, ymax=1): def __init__(self, xmin=0, ymin=0, xmax=1, ymax=1):
"""
Create the chart bounds with min max horizontal
and vertical values
"""
self._xmin = xmin self._xmin = xmin
self._ymin = ymin self._ymin = ymin
self._xmax = xmax self._xmax = xmax
self._ymax = ymax self._ymax = ymax
def set_polar_box(self, rmin=0, rmax=1, tmin=0, tmax=2 * pi): def set_polar_box(self, rmin=0, rmax=1, tmin=0, tmax=2 * pi):
"""Helper for polar charts"""
self._rmin = rmin self._rmin = rmin
self._rmax = rmax self._rmax = rmax
self._tmin = tmin self._tmin = tmin
@ -63,37 +72,45 @@ class Box(object):
@property @property
def xmin(self): def xmin(self):
"""X minimum getter"""
return self._xmin return self._xmin
@xmin.setter @xmin.setter
def xmin(self, value): def xmin(self, value):
"""X minimum setter"""
if value: if value:
self._xmin = value self._xmin = value
@property @property
def ymin(self): def ymin(self):
"""Y minimum getter"""
return self._ymin return self._ymin
@ymin.setter @ymin.setter
def ymin(self, value): def ymin(self, value):
"""Y minimum setter"""
if value: if value:
self._ymin = value self._ymin = value
@property @property
def xmax(self): def xmax(self):
"""X maximum getter"""
return self._xmax return self._xmax
@xmax.setter @xmax.setter
def xmax(self, value): def xmax(self, value):
"""X maximum setter"""
if value: if value:
self._xmax = value self._xmax = value
@property @property
def ymax(self): def ymax(self):
"""Y maximum getter"""
return self._ymax return self._ymax
@ymax.setter @ymax.setter
def ymax(self, value): def ymax(self, value):
"""Y maximum setter"""
if value: if value:
self._ymax = value self._ymax = value
@ -129,8 +146,11 @@ class Box(object):
class View(object): class View(object):
"""Projection base class""" """Projection base class"""
def __init__(self, width, height, box): def __init__(self, width, height, box):
"""Create the view with a width an height and a box bounds"""
self.width = width self.width = width
self.height = height self.height = height
self.box = box self.box = box
@ -156,14 +176,22 @@ class View(object):
class ReverseView(View): class ReverseView(View):
"""Same as view but reversed vertically"""
def y(self, y): def y(self, y):
"""Project reversed y"""
if y is None: if y is None:
return None return None
return (self.height * (y - self.box.ymin) / self.box.height) return (self.height * (y - self.box.ymin) / self.box.height)
class HorizontalView(View): class HorizontalView(View):
"""Same as view but transposed"""
def __init__(self, width, height, box): def __init__(self, width, height, box):
"""Create the view with a width an height and a box bounds"""
self._force_vertical = None self._force_vertical = None
self.width = width self.width = width
self.height = height self.height = height
@ -173,7 +201,7 @@ class HorizontalView(View):
self.box.swap() self.box.swap()
def x(self, x): def x(self, x):
"""Project x""" """Project x as y"""
if x is None: if x is None:
return None return None
if self._force_vertical: if self._force_vertical:
@ -181,6 +209,7 @@ class HorizontalView(View):
return super(HorizontalView, self).y(x) return super(HorizontalView, self).y(x)
def y(self, y): def y(self, y):
"""Project y as x"""
if y is None: if y is None:
return None return None
if self._force_vertical: if self._force_vertical:
@ -189,6 +218,7 @@ class HorizontalView(View):
class PolarView(View): class PolarView(View):
"""Polar projection for pie like graphs""" """Polar projection for pie like graphs"""
def __call__(self, rhotheta): def __call__(self, rhotheta):
@ -201,9 +231,11 @@ class PolarView(View):
class PolarLogView(View): class PolarLogView(View):
"""Logarithmic polar projection""" """Logarithmic polar projection"""
def __init__(self, width, height, box): def __init__(self, width, height, box):
"""Create the view with a width an height and a box bounds"""
super(PolarLogView, self).__init__(width, height, box) super(PolarLogView, self).__init__(width, height, box)
if not hasattr(box, '_rmin') or not hasattr(box, '_rmax'): if not hasattr(box, '_rmin') or not hasattr(box, '_rmax'):
raise Exception( raise Exception(
@ -230,9 +262,11 @@ class PolarLogView(View):
class PolarThetaView(View): class PolarThetaView(View):
"""Logarithmic polar projection""" """Logarithmic polar projection"""
def __init__(self, width, height, box, aperture=pi / 3): def __init__(self, width, height, box, aperture=pi / 3):
"""Create the view with a width an height and a box bounds"""
super(PolarThetaView, self).__init__(width, height, box) super(PolarThetaView, self).__init__(width, height, box)
self.aperture = aperture self.aperture = aperture
if not hasattr(box, '_tmin') or not hasattr(box, '_tmax'): if not hasattr(box, '_tmin') or not hasattr(box, '_tmax'):
@ -253,9 +287,11 @@ class PolarThetaView(View):
class PolarThetaLogView(View): class PolarThetaLogView(View):
"""Logarithmic polar projection""" """Logarithmic polar projection"""
def __init__(self, width, height, box, aperture=pi / 3): def __init__(self, width, height, box, aperture=pi / 3):
"""Create the view with a width an height and a box bounds"""
super(PolarThetaLogView, self).__init__(width, height, box) super(PolarThetaLogView, self).__init__(width, height, box)
self.aperture = aperture self.aperture = aperture
if not hasattr(box, '_tmin') or not hasattr(box, '_tmax'): if not hasattr(box, '_tmin') or not hasattr(box, '_tmax'):
@ -288,9 +324,12 @@ class PolarThetaLogView(View):
class LogView(View): class LogView(View):
"""Logarithmic projection """
"""Y Logarithmic projection"""
# Do not want to call the parent here # Do not want to call the parent here
def __init__(self, width, height, box): def __init__(self, width, height, box):
"""Create the view with a width an height and a box bounds"""
self.width = width self.width = width
self.height = height self.height = height
self.box = box self.box = box
@ -310,9 +349,12 @@ class LogView(View):
class XLogView(View): class XLogView(View):
"""Logarithmic projection """
"""X logarithmic projection"""
# Do not want to call the parent here # Do not want to call the parent here
def __init__(self, width, height, box): def __init__(self, width, height, box):
"""Create the view with a width an height and a box bounds"""
self.width = width self.width = width
self.height = height self.height = height
self.box = box self.box = box
@ -330,7 +372,11 @@ class XLogView(View):
class XYLogView(XLogView, LogView): class XYLogView(XLogView, LogView):
"""X and Y logarithmic projection"""
def __init__(self, width, height, box): def __init__(self, width, height, box):
"""Create the view with a width an height and a box bounds"""
self.width = width self.width = width
self.height = height self.height = height
self.box = box self.box = box
@ -342,9 +388,12 @@ class XYLogView(XLogView, LogView):
class HorizontalLogView(XLogView): class HorizontalLogView(XLogView):
"""Logarithmic projection """
"""Transposed Logarithmic projection"""
# Do not want to call the parent here # Do not want to call the parent here
def __init__(self, width, height, box): def __init__(self, width, height, box):
"""Create the view with a width an height and a box bounds"""
self._force_vertical = None self._force_vertical = None
self.width = width self.width = width
self.height = height self.height = height
@ -357,7 +406,7 @@ class HorizontalLogView(XLogView):
self.box.swap() self.box.swap()
def x(self, x): def x(self, x):
"""Project x""" """Project x as y"""
if x is None: if x is None:
return None return None
if self._force_vertical: if self._force_vertical:
@ -365,6 +414,7 @@ class HorizontalLogView(XLogView):
return super(XLogView, self).y(x) return super(XLogView, self).y(x)
def y(self, y): def y(self, y):
"""Project y as x"""
if y is None: if y is None:
return None return None
if self._force_vertical: if self._force_vertical:

Loading…
Cancel
Save