예제 #1
0
    def __init__(self, config, series, secondary_series, uuid, xml_filters):
        """Init the graph"""
        self.uuid = uuid
        self.__dict__.update(config.to_dict())
        self.config = config
        self.series = series or []
        self.secondary_series = secondary_series or []
        self.xml_filters = xml_filters or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin = Margin(*([self.margin] * 4))
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            if self._dual:
                get = lambda x: x[1] or 1
            else:
                get = lambda x: x

            positive_values = list(filter(
                lambda x: x > 0,
                [get(val)
                 for serie in self.series for val in serie.safe_values]))

            self.zero = min(positive_values or (1,)) or 1
        self._draw()
        self.svg.pre_render()
예제 #2
0
파일: base.py 프로젝트: signed0/pygal
    def __init__(self, config, series, secondary_series, uuid):
        """Init the graph"""
        self.uuid = uuid
        self.config = config
        self.series = series or []
        self.secondary_series = secondary_series or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin = Margin(*([self.margin] * 4))
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            from pygal.graph.xy import XY
            if isinstance(self, XY):
                get = lambda x: x[1]
            else:
                get = lambda x: x

            positive_values = list(
                filter(lambda x: x > 0, [
                    get(val) for serie in self.series
                    for val in serie.safe_values
                ]))

            self.zero = min(positive_values) if positive_values else 0
        self._draw()
        self.svg.pre_render()
예제 #3
0
파일: base.py 프로젝트: AlexSnet/pygal
    def __init__(self, config, series, secondary_series, uuid, xml_filters):
        """Init the graph"""
        self.uuid = uuid
        self.__dict__.update(config.to_dict())
        self.config = config
        self.series = series or []
        self.secondary_series = secondary_series or []
        self.xml_filters = xml_filters or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin = Margin(*([self.margin] * 4))
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            if self._dual:
                get = lambda x: x[1] or 1
            else:
                get = lambda x: x

            positive_values = list(filter(
                lambda x: x > 0,
                [get(val)
                 for serie in self.series for val in serie.safe_values]))

            self.zero = min(positive_values or 1,) or 1
        self._draw()
        self.svg.pre_render()
예제 #4
0
파일: base.py 프로젝트: flg8r96/pygal
    def __init__(self, config, series, secondary_series):
        """Init the graph"""
        self.config = config
        self.series = series or []
        self.secondary_series = secondary_series or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin = Margin(*([20] * 4))
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            from pygal.graph.xy import XY
            if isinstance(self, XY):
                get = lambda x: x[1]
            else:
                get = lambda x: x

            positive_values = list(filter(
                lambda x: x > 0,
                [get(val)
                 for serie in self.series for val in serie.safe_values]))

            self.zero = min(positive_values) if positive_values else 0
        self._draw()
        self.svg.pre_render()
예제 #5
0
파일: base.py 프로젝트: yzhen-hit/pygal
    def setup(self, **kwargs):
        """Set up the transient state prior rendering"""
        # Keep labels in case of map
        if getattr(self, 'x_labels', None) is not None:
            self.x_labels = list(self.x_labels)
        if getattr(self, 'y_labels', None) is not None:
            self.y_labels = list(self.y_labels)
        self.state = State(self, **kwargs)
        if isinstance(self.style, type):
            self.style = self.style()
        self.series = self.prepare_values([
            rs for rs in self.raw_series if not rs[1].get('secondary')
        ]) or []
        self.secondary_series = self.prepare_values([
            rs for rs in self.raw_series if rs[1].get('secondary')
        ], len(self.series)) or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin_box = Margin(
            self.margin_top or self.margin, self.margin_right or self.margin,
            self.margin_bottom or self.margin, self.margin_left or self.margin
        )
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            positive_values = list(
                filter(
                    lambda x: x > 0, [
                        val[1] or 1 if self._dual else val
                        for serie in self.series for val in serie.safe_values
                    ]
                )
            )

            self.zero = min(positive_values or (1, )) or 1
        if self._len < 3:
            self.interpolate = None
        self._draw()
        self.svg.pre_render()
예제 #6
0
파일: base.py 프로젝트: Zanarh/pygal
    def __init__(self, config, series):
        """Init the graph"""
        self.config = config
        self.series = series
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self.nodes = {}
        self.margin = Margin(*([20] * 4))
        self._box = Box()
        self.view = None

        if self.series and self._has_data():
            self._draw()
        else:
            self.svg.draw_no_data()

        self.svg.pre_render()
예제 #7
0
 def __init__(self, config, series):
     """Init the graph"""
     self.config = config
     self.series = series or []
     self.horizontal = getattr(self, 'horizontal', False)
     self.svg = Svg(self)
     self._x_labels = None
     self._y_labels = None
     self.nodes = {}
     self.margin = Margin(*([20] * 4))
     self._box = Box()
     self.view = None
     if self.logarithmic and self.zero == 0:
         # Explicit min to avoid interpolation dependency
         self.zero = min(
             filter(lambda x: x > 0, [
                 val for serie in self.series for val in serie.safe_values
             ]))
     self._draw()
     self.svg.pre_render()
예제 #8
0
파일: base.py 프로젝트: rjmcguire/pygal
 def __init__(self, config=None, **kwargs):
     """Init the graph"""
     self.config = config or Config()
     self.config(**kwargs)
     self.horizontal = getattr(self, 'horizontal', False)
     self.svg = Svg(self)
     self.series = []
     self._x_labels = None
     self._y_labels = None
     self._box = None
     self.nodes = {}
     self.margin = None
     self.view = None
예제 #9
0
파일: base.py 프로젝트: rayleyva/pygal
    def __init__(self, config, series):
        """Init the graph"""
        self.config = config
        self.series = series
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self.nodes = {}
        self.margin = Margin(*([20] * 4))
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            self.zero = min(val for serie in self.series
                            for val in serie.values)

        if self.series and self._has_data():
            self._draw()
        else:
            self.svg.draw_no_data()

        self.svg.pre_render()
예제 #10
0
 def __init__(self, config, series):
     """Init the graph"""
     self.config = config
     self.series = series or []
     self.horizontal = getattr(self, 'horizontal', False)
     self.svg = Svg(self)
     self._x_labels = None
     self._y_labels = None
     self.nodes = {}
     self.margin = Margin(*([20] * 4))
     self._box = Box()
     self.view = None
     if self.logarithmic and self.zero == 0:
         # Explicit min to avoid interpolation dependency
         self.zero = min([x for x in [val for serie in self.series for val in serie.safe_values] if x > 0])
     self._draw()
     self.svg.pre_render()
예제 #11
0
파일: base.py 프로젝트: Kozea/pygal
    def setup(self, **kwargs):
        """Set up the transient state prior rendering"""
        # Keep labels in case of map
        if getattr(self, 'x_labels', None) is not None:
            self.x_labels = list(self.x_labels)
        if getattr(self, 'y_labels', None) is not None:
            self.y_labels = list(self.y_labels)
        self.state = State(self, **kwargs)
        if isinstance(self.style, type):
            self.style = self.style()
        self.series = self.prepare_values([
            rs for rs in self.raw_series if not rs[1].get('secondary')
        ]) or []
        self.secondary_series = self.prepare_values([
            rs for rs in self.raw_series if rs[1].get('secondary')
        ], len(self.series)) or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin_box = Margin(
            self.margin_top or self.margin, self.margin_right or self.margin,
            self.margin_bottom or self.margin, self.margin_left or self.margin
        )
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            positive_values = list(
                filter(
                    lambda x: x > 0, [
                        val[1] or 1 if self._dual else val
                        for serie in self.series for val in serie.safe_values
                    ]
                )
            )

            self.zero = min(positive_values or (1, )) or 1
        if self._len < 3:
            self.interpolate = None
        self._draw()
        self.svg.pre_render()
예제 #12
0
파일: base.py 프로젝트: Zanarh/pygal
    def __init__(self, config, series):
        """Init the graph"""
        self.config = config
        self.series = series
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self.nodes = {}
        self.margin = Margin(*([20] * 4))
        self._box = Box()
        self.view = None

        if self.series and self._has_data():
            self._draw()
        else:
            self.svg.draw_no_data()

        self.svg.pre_render()
예제 #13
0
파일: base.py 프로젝트: anasrazeq/pygal
    def setup(self, **kwargs):
        """Init the graph"""
        # Keep labels in case of map
        if getattr(self, 'x_labels', None) is not None:
            self.x_labels = list(map(to_unicode, self.x_labels))
        if getattr(self, 'y_labels', None) is not None:
            self.y_labels = list(self.y_labels)
        self.state = State(self, **kwargs)
        self.series = self.prepare_values(
            self.raw_series) or []
        self.secondary_series = self.prepare_values(
            self.raw_series2, len(self.series)) or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin_box = Margin(
            self.margin_top or self.margin,
            self.margin_right or self.margin,
            self.margin_bottom or self.margin,
            self.margin_left or self.margin)
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            positive_values = list(filter(
                lambda x: x > 0,
                [val[1] or 1 if self._dual else val
                 for serie in self.series for val in serie.safe_values]))

            self.zero = min(positive_values or (1,)) or 1
        if self._len < 3:
            self.interpolate = None
        self._draw()
        self.svg.pre_render()
예제 #14
0
파일: base.py 프로젝트: rjmcguire/pygal
class BaseGraph(object):
    """Graphs commons"""

    __value__ = Value

    def __init__(self, config=None, **kwargs):
        """Init the graph"""
        self.config = config or Config()
        self.config(**kwargs)
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self.series = []
        self._x_labels = None
        self._y_labels = None
        self._box = None
        self.nodes = {}
        self.margin = None
        self.view = None

    def add(self, title, values):
        """Add a serie to this graph"""
        self.series.append(
            Serie(title, values, len(self.series), self.__value__))

    def reinit(self):
        """(Re-)Init the graph"""
        self.margin = Margin(*([20] * 4))
        self._box = Box()
        if self.logarithmic and self.zero == 0:
            # If logarithmic, default zero to 1
            self.zero = 1

    def __getattr__(self, attr):
        """Search in config, then in self"""
        if attr in dir(self.config):
            return object.__getattribute__(self.config, attr)
        return object.__getattribute__(self, attr)

    @property
    def _format(self):
        """Return the value formatter for this graph"""
        return humanize if self.human_readable else str

    def _compute(self):
        """Initial computations to draw the graph"""

    def _plot(self):
        """Actual plotting of the graph"""

    def _compute_margin(self):
        """Compute graph margins from set texts"""
        if self.show_legend:
            h, w = get_texts_box(
                map(lambda x: truncate(x, self.truncate_legend or 15),
                  cut(self.series, 'title')),
                self.legend_font_size)
            if self.legend_at_bottom:
                h_max = max(h, self.legend_box_size)
                self.margin.bottom += 10 + h_max * round(
                        sqrt(len(self.series)) - 1) * 1.5 + h_max
            else:
                self.margin.right += 10 + w + self.legend_box_size

        if self.title:
            h, w = get_text_box(self.title, self.title_font_size)
            self.margin.top += 10 + h

        if self._x_labels:
            h, w = get_texts_box(
                cut(self._x_labels), self.label_font_size)
            self._x_labels_height = 10 + max(
                w * sin(rad(self.x_label_rotation)), h)
            self.margin.bottom += self._x_labels_height
            if self.x_label_rotation:
                self.margin.right = max(
                    w * cos(rad(self.x_label_rotation)),
                    self.margin.right)
        else:
            self._x_labels_height = 0
        if self._y_labels:
            h, w = get_texts_box(
                cut(self._y_labels), self.label_font_size)
            self.margin.left += 10 + max(
                w * cos(rad(self.y_label_rotation)), h)

    @cached_property
    def _legends(self):
        """Getter for series title"""
        return [serie.title for serie in self.series]

    @cached_property
    def _values(self):
        """Getter for series values (flattened)"""
        return [val
                for serie in self.series
                for val in serie.values
                if val != None]

    @cached_property
    def _len(self):
        """Getter for the maximum series size"""
        return max([len(serie.values) for serie in self.series])

    @cached_property
    def _min(self):
        """Getter for the minimum series value"""
        return (self.range and self.range[0]) or min(self._values)

    @cached_property
    def _max(self):
        """Getter for the maximum series value"""
        return (self.range and self.range[1]) or max(self._values)

    def _draw(self):
        """Draw all the things"""
        self._prepare_data()
        self._compute()
        self._compute_margin()
        self._decorate()
        self._plot()

    def _has_data(self):
        """Check if there is any data"""
        if len(self.series) == 0:
            return False
        if sum(map(len, map(lambda s: s.values, self.series))) == 0:
            return False
        return True

    def _prepare_data(self):
        """Remove aberrant values"""
        if self.logarithmic:
            for serie in self.series:
                for metadata in serie.metadata:
                    if metadata.value <= 0:
                        metadata.value = None

    def _uniformize_data(self):
        """Make all series to max len"""
        for serie in self.series:
            if len(serie.values) < self._len:
                diff = self._len - len(serie.values)
                serie.metadata += diff * [self.__value__(0)]

            for metadata in serie.metadata:
                if metadata.value == None:
                    metadata.value = 0

    def _render(self):
        """Make the graph internally"""
        self.reinit()
        self.svg.reinit()
        if self._has_data():
            self._draw()
            self.svg.pre_render(False)
        else:
            self.svg.pre_render(True)

    def render(self, is_unicode=False):
        """Render the graph, and return the svg string"""
        self._render()
        return self.svg.render(
            is_unicode=is_unicode, pretty_print=self.pretty_print)

    def render_tree(self):
        """Render the graph, and return lxml tree"""
        self._render()
        return self.svg.root

    def render_pyquery(self):
        """Render the graph, and return a pyquery wrapped tree"""
        from pyquery import PyQuery as pq
        return pq(self.render_tree())

    def render_in_browser(self):
        """Render the graph, open it in your browser with black magic"""
        from lxml.html import open_in_browser
        open_in_browser(self.render_tree(), encoding='utf-8')

    def render_response(self):
        """Render the graph, and return a Flask response"""
        from flask import Response
        return Response(self.render(), mimetype='image/svg+xml')

    def render_to_file(self, filename):
        """Render the graph, and write it to filename"""
        with io.open(filename, 'w', encoding='utf-8') as f:
            f.write(self.render(is_unicode=True))

    def render_to_png(self, filename):
        """Render the graph, convert it to png and write it to filename"""
        import cairosvg
        from io import BytesIO
        fakefile = BytesIO()
        fakefile.write(self.render())
        fakefile.seek(0)
        cairosvg.surface.PNGSurface.convert(
            file_obj=fakefile, write_to=filename)
예제 #15
0
파일: base.py 프로젝트: Zanarh/pygal
class BaseGraph(object):
    """Graphs commons"""

    _adapters = []

    def __init__(self, config, series):
        """Init the graph"""
        self.config = config
        self.series = series
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self.nodes = {}
        self.margin = Margin(*([20] * 4))
        self._box = Box()
        self.view = None

        if self.series and self._has_data():
            self._draw()
        else:
            self.svg.draw_no_data()

        self.svg.pre_render()

    def __getattr__(self, attr):
        """Search in config, then in self"""
        if attr in dir(self.config):
            return object.__getattribute__(self.config, attr)
        return object.__getattribute__(self, attr)

    @property
    def _format(self):
        """Return the value formatter for this graph"""
        return humanize if self.human_readable else str

    def _compute(self):
        """Initial computations to draw the graph"""

    def _plot(self):
        """Actual plotting of the graph"""

    def _compute_margin(self):
        """Compute graph margins from set texts"""
        if self.show_legend:
            h, w = get_texts_box(
                map(lambda x: truncate(x, self.truncate_legend or 15),
                    cut(self.series, 'title')), self.legend_font_size)
            if self.legend_at_bottom:
                h_max = max(h, self.legend_box_size)
                self.margin.bottom += 10 + h_max * round(
                    sqrt(self._order) - 1) * 1.5 + h_max
            else:
                self.margin.right += 10 + w + self.legend_box_size

        if self.title:
            h, w = get_text_box(self.title, self.title_font_size)
            self.margin.top += 10 + h

        if self._x_labels:
            h, w = get_texts_box(cut(self._x_labels), self.label_font_size)
            self._x_labels_height = 10 + max(
                w * sin(rad(self.x_label_rotation)), h)
            self.margin.bottom += self._x_labels_height
            if self.x_label_rotation:
                self.margin.right = max(w * cos(rad(self.x_label_rotation)),
                                        self.margin.right)
        else:
            self._x_labels_height = 0
        if self._y_labels:
            h, w = get_texts_box(cut(self._y_labels), self.label_font_size)
            self.margin.left += 10 + max(w * cos(rad(self.y_label_rotation)),
                                         h)

    @cached_property
    def _legends(self):
        """Getter for series title"""
        return [serie.title for serie in self.series]

    @cached_property
    def _values(self):
        """Getter for series values (flattened)"""
        return [
            val for serie in self.series for val in serie.values
            if val is not None
        ]

    @cached_property
    def _len(self):
        """Getter for the maximum series size"""
        return max([len(serie.values) for serie in self.series])

    @cached_property
    def _min(self):
        """Getter for the minimum series value"""
        return (self.range and self.range[0]) or min(self._values)

    @cached_property
    def _max(self):
        """Getter for the maximum series value"""
        return (self.range and self.range[1]) or max(self._values)

    @cached_property
    def _order(self):
        """Getter for the maximum series value"""
        return len(self.series)

    def _draw(self):
        """Draw all the things"""
        self._compute()
        self._compute_margin()
        self._decorate()
        self._plot()

    def _has_data(self):
        """Check if there is any data"""
        return sum(map(len, map(lambda s: s.values, self.series))) != 0

    def render(self, is_unicode):
        """Render the graph, and return the svg string"""
        return self.svg.render(is_unicode=is_unicode,
                               pretty_print=self.pretty_print)

    def render_tree(self):
        """Render the graph, and return lxml tree"""
        return self.svg.root
예제 #16
0
파일: base.py 프로젝트: Zanarh/pygal
class BaseGraph(object):
    """Graphs commons"""

    _adapters = []

    def __init__(self, config, series):
        """Init the graph"""
        self.config = config
        self.series = series
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self.nodes = {}
        self.margin = Margin(*([20] * 4))
        self._box = Box()
        self.view = None

        if self.series and self._has_data():
            self._draw()
        else:
            self.svg.draw_no_data()

        self.svg.pre_render()

    def __getattr__(self, attr):
        """Search in config, then in self"""
        if attr in dir(self.config):
            return object.__getattribute__(self.config, attr)
        return object.__getattribute__(self, attr)

    @property
    def _format(self):
        """Return the value formatter for this graph"""
        return humanize if self.human_readable else str

    def _compute(self):
        """Initial computations to draw the graph"""

    def _plot(self):
        """Actual plotting of the graph"""

    def _compute_margin(self):
        """Compute graph margins from set texts"""
        if self.show_legend:
            h, w = get_texts_box(
                map(lambda x: truncate(x, self.truncate_legend or 15),
                    cut(self.series, 'title')),
                self.legend_font_size)
            if self.legend_at_bottom:
                h_max = max(h, self.legend_box_size)
                self.margin.bottom += 10 + h_max * round(
                    sqrt(self._order) - 1) * 1.5 + h_max
            else:
                self.margin.right += 10 + w + self.legend_box_size

        if self.title:
            h, w = get_text_box(self.title, self.title_font_size)
            self.margin.top += 10 + h

        if self._x_labels:
            h, w = get_texts_box(
                cut(self._x_labels), self.label_font_size)
            self._x_labels_height = 10 + max(
                w * sin(rad(self.x_label_rotation)), h)
            self.margin.bottom += self._x_labels_height
            if self.x_label_rotation:
                self.margin.right = max(
                    w * cos(rad(self.x_label_rotation)),
                    self.margin.right)
        else:
            self._x_labels_height = 0
        if self._y_labels:
            h, w = get_texts_box(
                cut(self._y_labels), self.label_font_size)
            self.margin.left += 10 + max(
                w * cos(rad(self.y_label_rotation)), h)

    @cached_property
    def _legends(self):
        """Getter for series title"""
        return [serie.title for serie in self.series]

    @cached_property
    def _values(self):
        """Getter for series values (flattened)"""
        return [val
                for serie in self.series
                for val in serie.values
                if val is not None]

    @cached_property
    def _len(self):
        """Getter for the maximum series size"""
        return max([len(serie.values) for serie in self.series])

    @cached_property
    def _min(self):
        """Getter for the minimum series value"""
        return (self.range and self.range[0]) or min(self._values)

    @cached_property
    def _max(self):
        """Getter for the maximum series value"""
        return (self.range and self.range[1]) or max(self._values)

    @cached_property
    def _order(self):
        """Getter for the maximum series value"""
        return len(self.series)

    def _draw(self):
        """Draw all the things"""
        self._compute()
        self._compute_margin()
        self._decorate()
        self._plot()

    def _has_data(self):
        """Check if there is any data"""
        return sum(map(len, map(lambda s: s.values, self.series))) != 0

    def render(self, is_unicode):
        """Render the graph, and return the svg string"""
        return self.svg.render(
            is_unicode=is_unicode, pretty_print=self.pretty_print)

    def render_tree(self):
        """Render the graph, and return lxml tree"""
        return self.svg.root
예제 #17
0
파일: base.py 프로젝트: yzhen-hit/pygal
class BaseGraph(object):
    """Chart internal behaviour related functions"""

    _adapters = []

    def __init__(self, config=None, **kwargs):
        """Config preparation and various initialization"""
        if config:
            if isinstance(config, type):
                config = config()
            else:
                config = config.copy()
        else:
            config = Config()

        config(**kwargs)
        self.config = config
        self.state = None
        self.uuid = str(uuid4())
        self.raw_series = []
        self.xml_filters = []

    def __setattr__(self, name, value):
        """Set an attribute on the class or in the state if there is one"""
        if name.startswith('__') or getattr(self, 'state', None) is None:
            super(BaseGraph, self).__setattr__(name, value)
        else:
            setattr(self.state, name, value)

    def __getattribute__(self, name):
        """Get an attribute from the class or from the state if there is one"""
        if name.startswith('__') or name == 'state' or getattr(
                self, 'state',
                None) is None or name not in self.state.__dict__:
            return super(BaseGraph, self).__getattribute__(name)
        return getattr(self.state, name)

    def prepare_values(self, raw, offset=0):
        """Prepare the values to start with sane values"""
        from pygal import Histogram
        from pygal.graph.map import BaseMap

        if self.zero == 0 and isinstance(self, BaseMap):
            self.zero = 1

        if self.x_label_rotation:
            self.x_label_rotation %= 360

        if self.y_label_rotation:
            self.y_label_rotation %= 360

        for key in ('x_labels', 'y_labels'):
            if getattr(self, key):
                setattr(self, key, list(getattr(self, key)))
        if not raw:
            return

        adapters = list(self._adapters) or [lambda x: x]
        if self.logarithmic:
            for fun in not_zero, positive:
                if fun in adapters:
                    adapters.remove(fun)
            adapters = adapters + [positive, not_zero]
        adapters = adapters + [decimal_to_float]

        self._adapt = reduce(compose, adapters) if not self.strict else ident
        self._x_adapt = reduce(
            compose, self._x_adapters
        ) if not self.strict and getattr(self, '_x_adapters', None) else ident

        series = []

        raw = [(
            list(raw_values) if not isinstance(raw_values, dict) else
            raw_values, serie_config_kwargs
        ) for raw_values, serie_config_kwargs in raw]

        width = max([len(values)
                     for values, _ in raw] + [len(self.x_labels or [])])

        for raw_values, serie_config_kwargs in raw:
            metadata = {}
            values = []
            if isinstance(raw_values, dict):
                if isinstance(self, BaseMap):
                    raw_values = list(raw_values.items())
                else:
                    value_list = [None] * width
                    for k, v in raw_values.items():
                        if k in (self.x_labels or []):
                            value_list[self.x_labels.index(k)] = v
                    raw_values = value_list

            for index, raw_value in enumerate(raw_values + (
                (width - len(raw_values)) * [None]  # aligning values
                    if len(raw_values) < width else [])):
                if isinstance(raw_value, dict):
                    raw_value = dict(raw_value)
                    value = raw_value.pop('value', None)
                    metadata[index] = raw_value
                else:
                    value = raw_value

                # Fix this by doing this in charts class methods
                if isinstance(self, Histogram):
                    if value is None:
                        value = (None, None, None)
                    elif not is_list_like(value):
                        value = (value, self.zero, self.zero)
                    elif len(value) == 2:
                        value = (1, value[0], value[1])
                    value = list(map(self._adapt, value))
                elif self._dual:
                    if value is None:
                        value = (None, None)
                    elif not is_list_like(value):
                        value = (value, self.zero)
                    if self._x_adapt:
                        value = (
                            self._x_adapt(value[0]), self._adapt(value[1])
                        )
                    if isinstance(self, BaseMap):
                        value = (self._adapt(value[0]), value[1])
                    else:
                        value = list(map(self._adapt, value))
                else:
                    value = self._adapt(value)

                values.append(value)
            serie_config = SerieConfig()
            serie_config(
                **dict((k, v) for k, v in self.state.__dict__.items()
                       if k in dir(serie_config))
            )
            serie_config(**serie_config_kwargs)
            series.append(
                Serie(offset + len(series), values, serie_config, metadata)
            )
        return series

    def setup(self, **kwargs):
        """Set up the transient state prior rendering"""
        # Keep labels in case of map
        if getattr(self, 'x_labels', None) is not None:
            self.x_labels = list(self.x_labels)
        if getattr(self, 'y_labels', None) is not None:
            self.y_labels = list(self.y_labels)
        self.state = State(self, **kwargs)
        if isinstance(self.style, type):
            self.style = self.style()
        self.series = self.prepare_values([
            rs for rs in self.raw_series if not rs[1].get('secondary')
        ]) or []
        self.secondary_series = self.prepare_values([
            rs for rs in self.raw_series if rs[1].get('secondary')
        ], len(self.series)) or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin_box = Margin(
            self.margin_top or self.margin, self.margin_right or self.margin,
            self.margin_bottom or self.margin, self.margin_left or self.margin
        )
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            positive_values = list(
                filter(
                    lambda x: x > 0, [
                        val[1] or 1 if self._dual else val
                        for serie in self.series for val in serie.safe_values
                    ]
                )
            )

            self.zero = min(positive_values or (1, )) or 1
        if self._len < 3:
            self.interpolate = None
        self._draw()
        self.svg.pre_render()

    def teardown(self):
        """Remove the transient state after rendering"""
        if os.getenv('PYGAL_KEEP_STATE'):
            return

        del self.state
        self.state = None

    def _repr_svg_(self):
        """Display svg in IPython notebook"""
        return self.render(disable_xml_declaration=True)

    def _repr_png_(self):
        """Display png in IPython notebook"""
        return self.render_to_png()
예제 #18
0
파일: base.py 프로젝트: SimonSapin/pygal
class BaseGraph(object):
    """Graphs commons"""

    _adapters = []

    def __init__(self, config, series, secondary_series, uuid):
        """Init the graph"""
        self.uuid = uuid
        self.__dict__.update(config.to_dict())
        self.config = config
        self.series = series or []
        self.secondary_series = secondary_series or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin = Margin(*([self.margin] * 4))
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            from pygal.graph.xy import XY
            if isinstance(self, XY):
                get = lambda x: x[1]
            else:
                get = lambda x: x

            positive_values = list(filter(
                lambda x: x > 0,
                [get(val)
                 for serie in self.series for val in serie.safe_values]))

            self.zero = min(positive_values) if positive_values else 0
        self._draw()
        self.svg.pre_render()

    @property
    def all_series(self):
        return self.series + self.secondary_series

    @property
    def _format(self):
        """Return the value formatter for this graph"""
        return self.value_formatter or (
            humanize if self.human_readable else str)

    def _compute(self):
        """Initial computations to draw the graph"""

    def _plot(self):
        """Actual plotting of the graph"""

    def _compute_margin(self):
        """Compute graph margins from set texts"""
        self._legend_at_left_width = 0
        for series_group in (self.series, self.secondary_series):
            if self.show_legend and series_group:
                h, w = get_texts_box(
                    map(lambda x: truncate(x, self.truncate_legend or 15),
                        cut(series_group, 'title')),
                    self.legend_font_size)
                if self.legend_at_bottom:
                    h_max = max(h, self.legend_box_size)
                    self.margin.bottom += self.spacing + h_max * round(
                        sqrt(self._order) - 1) * 1.5 + h_max
                else:
                    if series_group is self.series:
                        legend_width = self.spacing + w + self.legend_box_size
                        self.margin.left += legend_width
                        self._legend_at_left_width += legend_width
                    else:
                        self.margin.right += (
                            self.spacing + w + self.legend_box_size)

        for xlabels in (self._x_labels, self._x_2nd_labels):
            if xlabels:
                h, w = get_texts_box(
                    map(lambda x: truncate(x, self.truncate_label or 25),
                        cut(xlabels)),
                    self.label_font_size)
                self._x_labels_height = self.spacing + max(
                    w * sin(rad(self.x_label_rotation)), h)
                if xlabels is self._x_labels:
                    self.margin.bottom += self._x_labels_height
                else:
                    self.margin.top += self._x_labels_height
                if self.x_label_rotation:
                    self.margin.right = max(
                        w * cos(rad(self.x_label_rotation)),
                        self.margin.right)
        if not self._x_labels:
            self._x_labels_height = 0

        if self.show_y_labels:
            for ylabels in (self._y_labels, self._y_2nd_labels):
                if ylabels:
                    h, w = get_texts_box(
                        cut(ylabels), self.label_font_size)
                    if ylabels is self._y_labels:
                        self.margin.left += self.spacing + max(
                            w * cos(rad(self.y_label_rotation)), h)
                    else:
                        self.margin.right += self.spacing + max(
                            w * cos(rad(self.y_label_rotation)), h)

        self.title = split_title(
            self.title, self.width, self.title_font_size)

        if self.title:
            h, _ = get_text_box(self.title[0], self.title_font_size)
            self.margin.top += len(self.title) * (self.spacing + h)

        self.x_title = split_title(
            self.x_title, self.width - self.margin.x, self.title_font_size)

        self._x_title_height = 0
        if self.x_title:
            h, _ = get_text_box(self.x_title[0], self.title_font_size)
            height = len(self.x_title) * (self.spacing + h)
            self.margin.bottom += height
            self._x_title_height = height + self.spacing

        self.y_title = split_title(
            self.y_title, self.height - self.margin.y, self.title_font_size)

        self._y_title_height = 0
        if self.y_title:
            h, _ = get_text_box(self.y_title[0], self.title_font_size)
            height = len(self.y_title) * (self.spacing + h)
            self.margin.left += height
            self._y_title_height = height + self.spacing

    @cached_property
    def _legends(self):
        """Getter for series title"""
        return [serie.title for serie in self.series]

    @cached_property
    def _secondary_legends(self):
        """Getter for series title on secondary y axis"""
        return [serie.title for serie in self.secondary_series]

    @cached_property
    def _values(self):
        """Getter for series values (flattened)"""
        return [val
                for serie in self.series
                for val in serie.values
                if val is not None]

    @cached_property
    def _secondary_values(self):
        """Getter for secondary series values (flattened)"""
        return [val
                for serie in self.secondary_series
                for val in serie.values
                if val is not None]

    @cached_property
    def _len(self):
        """Getter for the maximum series size"""
        return max([
            len(serie.values)
            for serie in self.all_series] or [0])

    @cached_property
    def _secondary_min(self):
        """Getter for the minimum series value"""
        return (self.range and self.range[0]) or (
            min(self._secondary_values) if self._secondary_values else None)

    @cached_property
    def _min(self):
        """Getter for the minimum series value"""
        return (self.range and self.range[0]) or (
            min(self._values) if self._values else None)

    @cached_property
    def _max(self):
        """Getter for the maximum series value"""
        return (self.range and self.range[1]) or (
            max(self._values) if self._values else None)

    @cached_property
    def _secondary_max(self):
        """Getter for the maximum series value"""
        return (self.range and self.range[1]) or (
            max(self._secondary_values) if self._secondary_values else None)

    @cached_property
    def _order(self):
        """Getter for the number of series"""
        return len(self.all_series)

    def _draw(self):
        """Draw all the things"""
        self._compute()
        self._compute_secondary()
        self._post_compute()
        self._compute_margin()
        self._decorate()
        if self.series and self._has_data():
            self._plot()
        else:
            self.svg.draw_no_data()

    def _has_data(self):
        """Check if there is any data"""
        return sum(
            map(len, map(lambda s: s.safe_values, self.series))) != 0 and (
                sum(map(abs, self._values)) != 0)

    def render(self, is_unicode=False):
        """Render the graph, and return the svg string"""
        return self.svg.render(
            is_unicode=is_unicode, pretty_print=self.pretty_print)

    def render_tree(self):
        """Render the graph, and return lxml tree"""
        return self.svg.root
예제 #19
0
파일: base.py 프로젝트: anasrazeq/pygal
class BaseGraph(object):
    """Graphs commons"""

    _adapters = []

    def __init__(self, config=None, **kwargs):
        if config:
            if isinstance(config, type):
                config = config()
            else:
                config = config.copy()
        else:
            config = Config()

        config(**kwargs)
        self.config = config
        self.state = None
        self.uuid = str(uuid4())
        self.raw_series = []
        self.raw_series2 = []
        self.xml_filters = []

    def __setattr__(self, name, value):
        if name.startswith('__') or getattr(self, 'state', None) is None:
            super(BaseGraph, self).__setattr__(name, value)
        else:
            setattr(self.state, name, value)

    def __getattribute__(self, name):
        if name.startswith('__') or name == 'state' or getattr(
                self, 'state', None
        ) is None or name not in self.state.__dict__:
            return super(BaseGraph, self).__getattribute__(name)
        return getattr(self.state, name)

    def add(self, title, values, **kwargs):
        """Add a serie to this graph"""
        if not is_list_like(values) and not isinstance(values, dict):
            values = [values]
        if kwargs.get('secondary', False):
            self.raw_series2.append((title, values, kwargs))
        else:
            self.raw_series.append((title, values, kwargs))

    def add_xml_filter(self, callback):
        self.xml_filters.append(callback)

    def prepare_values(self, raw, offset=0):
        """Prepare the values to start with sane values"""
        from pygal.graph.map import BaseMap
        from pygal import Histogram

        if self.zero == 0 and isinstance(self, BaseMap):
            self.zero = 1

        for key in ('x_labels', 'y_labels'):
            if getattr(self, key):
                setattr(self, key, list(getattr(self, key)))
        if not raw:
            return

        adapters = list(self._adapters) or [lambda x:x]
        if self.logarithmic:
            for fun in not_zero, positive:
                if fun in adapters:
                    adapters.remove(fun)
            adapters = adapters + [positive, not_zero]
        adapters = adapters + [decimal_to_float]
        adapter = reduce(compose, adapters) if not self.strict else ident
        x_adapter = reduce(
            compose, self._x_adapters) if getattr(
                self, '_x_adapters', None) else None
        series = []

        raw = [(
            title,
            list(raw_values) if not isinstance(
                raw_values, dict) else raw_values,
            serie_config_kwargs
        ) for title, raw_values, serie_config_kwargs in raw]

        width = max([len(values) for _, values, _ in raw] +
                    [len(self.x_labels or [])])

        for title, raw_values, serie_config_kwargs in raw:
            metadata = {}
            values = []
            if isinstance(raw_values, dict):
                if isinstance(self, BaseMap):
                    raw_values = list(raw_values.items())
                else:
                    value_list = [None] * width
                    for k, v in raw_values.items():
                        if k in self.x_labels:
                            value_list[self.x_labels.index(k)] = v
                    raw_values = value_list

            for index, raw_value in enumerate(
                    raw_values + (
                        (width - len(raw_values)) * [None]  # aligning values
                        if len(raw_values) < width else [])):
                if isinstance(raw_value, dict):
                    raw_value = dict(raw_value)
                    value = raw_value.pop('value', None)
                    metadata[index] = raw_value
                else:
                    value = raw_value

                # Fix this by doing this in charts class methods
                if isinstance(self, Histogram):
                    if value is None:
                        value = (None, None, None)
                    elif not is_list_like(value):
                        value = (value, self.zero, self.zero)
                    value = list(map(adapter, value))
                elif self._dual:
                    if value is None:
                        value = (None, None)
                    elif not is_list_like(value):
                        value = (value, self.zero)
                    if x_adapter:
                        value = (x_adapter(value[0]), adapter(value[1]))
                    if isinstance(self, BaseMap):
                        value = (adapter(value[0]), value[1])
                    else:
                        value = list(map(adapter, value))
                else:
                    value = adapter(value)

                values.append(value)
            serie_config = SerieConfig()
            serie_config(**dict((k, v) for k, v in self.state.__dict__.items()
                              if k in dir(serie_config)))
            serie_config(**serie_config_kwargs)
            series.append(
                Serie(offset + len(series),
                      title, values, serie_config, metadata))
        return series

    def setup(self, **kwargs):
        """Init the graph"""
        # Keep labels in case of map
        if getattr(self, 'x_labels', None) is not None:
            self.x_labels = list(map(to_unicode, self.x_labels))
        if getattr(self, 'y_labels', None) is not None:
            self.y_labels = list(self.y_labels)
        self.state = State(self, **kwargs)
        self.series = self.prepare_values(
            self.raw_series) or []
        self.secondary_series = self.prepare_values(
            self.raw_series2, len(self.series)) or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin_box = Margin(
            self.margin_top or self.margin,
            self.margin_right or self.margin,
            self.margin_bottom or self.margin,
            self.margin_left or self.margin)
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            positive_values = list(filter(
                lambda x: x > 0,
                [val[1] or 1 if self._dual else val
                 for serie in self.series for val in serie.safe_values]))

            self.zero = min(positive_values or (1,)) or 1
        if self._len < 3:
            self.interpolate = None
        self._draw()
        self.svg.pre_render()

    def teardown(self):
        if os.getenv('PYGAL_KEEP_STATE'):
            return

        del self.state
        self.state = None

    def render(self, is_unicode=False, **kwargs):
        """Render the graph, and return the svg string"""
        self.setup(**kwargs)
        svg = self.svg.render(
            is_unicode=is_unicode, pretty_print=self.pretty_print)
        self.teardown()
        return svg

    def render_tree(self):
        """Render the graph, and return (l)xml etree"""
        self.setup()
        svg = self.svg.root
        for f in self.xml_filters:
            svg = f(svg)
        self.teardown()
        return svg

    def render_table(self, **kwargs):
        # Import here to avoid lxml import
        try:
            from pygal.table import Table
        except ImportError:
            raise ImportError('You must install lxml to use render table')
        return Table(self).render(**kwargs)

    def render_pyquery(self):
        """Render the graph, and return a pyquery wrapped tree"""
        from pyquery import PyQuery as pq
        return pq(self.render(), parser='html')

    def render_in_browser(self, **kwargs):
        """Render the graph, open it in your browser with black magic"""
        try:
            from lxml.html import open_in_browser
        except ImportError:
            raise ImportError('You must install lxml to use render in browser')
        open_in_browser(self.render_tree(**kwargs), encoding='utf-8')

    def render_response(self, **kwargs):
        """Render the graph, and return a Flask response"""
        from flask import Response
        return Response(self.render(**kwargs), mimetype='image/svg+xml')

    def render_django_response(self, **kwargs):
        """Render the graph, and return a Django response"""
        from django.http import HttpResponse
        return HttpResponse(
            self.render(**kwargs), content_type='image/svg+xml')

    def render_to_file(self, filename, **kwargs):
        """Render the graph, and write it to filename"""
        with io.open(filename, 'w', encoding='utf-8') as f:
            f.write(self.render(is_unicode=True, **kwargs))

    def render_to_png(self, filename=None, dpi=72, **kwargs):
        """Render the graph, convert it to png and write it to filename"""
        import cairosvg
        return cairosvg.svg2png(
            bytestring=self.render(**kwargs), write_to=filename, dpi=dpi)

    def render_sparktext(self, relative_to=None):
        """Make a mini text sparkline from chart"""
        bars = u('▁▂▃▄▅▆▇█')
        if len(self.raw_series) == 0:
            return u('')
        values = list(self.raw_series[0][1])
        if len(values) == 0:
            return u('')

        chart = u('')
        values = list(map(lambda x: max(x, 0), values))

        vmax = max(values)
        if relative_to is None:
            relative_to = min(values)

        if (vmax - relative_to) == 0:
            chart = bars[0] * len(values)
            return chart

        divisions = len(bars) - 1
        for value in values:
            chart += bars[int(divisions *
                              (value - relative_to) / (vmax - relative_to))]
        return chart

    def render_sparkline(self, **kwargs):
        spark_options = dict(
            width=200,
            height=50,
            show_dots=False,
            show_legend=False,
            show_x_labels=False,
            show_y_labels=False,
            spacing=0,
            margin=5,
            explicit_size=True
        )
        spark_options.update(kwargs)
        return self.render(**spark_options)

    def _repr_svg_(self):
        """Display svg in IPython notebook"""
        return self.render(disable_xml_declaration=True)

    def _repr_png_(self):
        """Display png in IPython notebook"""
        return self.render_to_png()
예제 #20
0
class BaseGraph(object):

    """Chart internal behaviour related functions"""

    _adapters = []

    def __init__(self, config=None, **kwargs):
        """Config preparation and various initialization"""
        if config:
            if isinstance(config, type):
                config = config()
            else:
                config = config.copy()
        else:
            config = Config()

        config(**kwargs)
        self.config = config
        self.state = None
        self.uuid = str(uuid4())
        self.raw_series = []
        self.xml_filters = []

    def __setattr__(self, name, value):
        """Set an attribute on the class or in the state if there is one"""
        if name.startswith('__') or getattr(self, 'state', None) is None:
            super(BaseGraph, self).__setattr__(name, value)
        else:
            setattr(self.state, name, value)

    def __getattribute__(self, name):
        """Get an attribute from the class or from the state if there is one"""
        if name.startswith('__') or name == 'state' or getattr(
                self, 'state', None
        ) is None or name not in self.state.__dict__:
            return super(BaseGraph, self).__getattribute__(name)
        return getattr(self.state, name)

    def prepare_values(self, raw, offset=0):
        """Prepare the values to start with sane values"""
        from pygal.graph.map import BaseMap
        from pygal import Histogram

        if self.zero == 0 and isinstance(self, BaseMap):
            self.zero = 1

        if self.x_label_rotation:
            self.x_label_rotation %= 360

        if self.y_label_rotation:
            self.y_label_rotation %= 360

        for key in ('x_labels', 'y_labels'):
            if getattr(self, key):
                setattr(self, key, list(getattr(self, key)))
        if not raw:
            return

        adapters = list(self._adapters) or [lambda x:x]
        if self.logarithmic:
            for fun in not_zero, positive:
                if fun in adapters:
                    adapters.remove(fun)
            adapters = adapters + [positive, not_zero]
        adapters = adapters + [decimal_to_float]

        self._adapt = reduce(compose, adapters) if not self.strict else ident
        self._x_adapt = reduce(
            compose, self._x_adapters) if not self.strict and getattr(
                self, '_x_adapters', None) else ident

        series = []

        raw = [(
            list(raw_values) if not isinstance(
                raw_values, dict) else raw_values,
            serie_config_kwargs
        ) for raw_values, serie_config_kwargs in raw]

        width = max([len(values) for values, _ in raw] +
                    [len(self.x_labels or [])])

        for raw_values, serie_config_kwargs in raw:
            metadata = {}
            values = []
            if isinstance(raw_values, dict):
                if isinstance(self, BaseMap):
                    raw_values = list(raw_values.items())
                else:
                    value_list = [None] * width
                    for k, v in raw_values.items():
                        if k in (self.x_labels or []):
                            value_list[self.x_labels.index(k)] = v
                    raw_values = value_list

            for index, raw_value in enumerate(
                    raw_values + (
                        (width - len(raw_values)) * [None]  # aligning values
                        if len(raw_values) < width else [])):
                if isinstance(raw_value, dict):
                    raw_value = dict(raw_value)
                    value = raw_value.pop('value', None)
                    metadata[index] = raw_value
                else:
                    value = raw_value

                # Fix this by doing this in charts class methods
                if isinstance(self, Histogram):
                    if value is None:
                        value = (None, None, None)
                    elif not is_list_like(value):
                        value = (value, self.zero, self.zero)
                    elif len(value) == 2:
                        value = (1, value[0], value[1])
                    value = list(map(self._adapt, value))
                elif self._dual:
                    if value is None:
                        value = (None, None)
                    elif not is_list_like(value):
                        value = (value, self.zero)
                    if self._x_adapt:
                        value = (
                            self._x_adapt(value[0]),
                            self._adapt(value[1]))
                    if isinstance(self, BaseMap):
                        value = (self._adapt(value[0]), value[1])
                    else:
                        value = list(map(self._adapt, value))
                else:
                    value = self._adapt(value)

                values.append(value)
            serie_config = SerieConfig()
            serie_config(**dict((k, v) for k, v in self.state.__dict__.items()
                                if k in dir(serie_config)))
            serie_config(**serie_config_kwargs)
            series.append(
                Serie(offset + len(series), values, serie_config, metadata))
        return series

    def setup(self, **kwargs):
        """Set up the transient state prior rendering"""
        # Keep labels in case of map
        if getattr(self, 'x_labels', None) is not None:
            self.x_labels = list(self.x_labels)
        if getattr(self, 'y_labels', None) is not None:
            self.y_labels = list(self.y_labels)
        self.state = State(self, **kwargs)
        if isinstance(self.style, type):
            self.style = self.style()
        self.series = self.prepare_values(
            [rs for rs in self.raw_series if not rs[1].get('secondary')]) or []
        self.secondary_series = self.prepare_values(
            [rs for rs in self.raw_series if rs[1].get('secondary')],
            len(self.series)) or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin_box = Margin(
            self.margin_top or self.margin,
            self.margin_right or self.margin,
            self.margin_bottom or self.margin,
            self.margin_left or self.margin)
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            positive_values = list(filter(
                lambda x: x > 0,
                [val[1] or 1 if self._dual else val
                 for serie in self.series for val in serie.safe_values]))

            self.zero = min(positive_values or (1,)) or 1
        if self._len < 3:
            self.interpolate = None
        self._draw()
        self.svg.pre_render()

    def teardown(self):
        """Remove the transient state after rendering"""
        if os.getenv('PYGAL_KEEP_STATE'):
            return

        del self.state
        self.state = None

    def _repr_svg_(self):
        """Display svg in IPython notebook"""
        return self.render(disable_xml_declaration=True)

    def _repr_png_(self):
        """Display png in IPython notebook"""
        return self.render_to_png()
예제 #21
0
파일: base.py 프로젝트: zicada/dokweb
class BaseGraph(object):
    """Graphs commons"""

    _adapters = []

    def __init__(self, config, series):
        """Init the graph"""
        self.config = config
        self.series = series or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self.nodes = {}
        self.margin = Margin(*([20] * 4))
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            self.zero = min(filter(
                lambda x: x > 0,
                [val for serie in self.series for val in serie.safe_values]))
        self._draw()
        self.svg.pre_render()

    def __getattr__(self, attr):
        """Search in config, then in self"""
        if attr in dir(self.config):
            return object.__getattribute__(self.config, attr)
        return object.__getattribute__(self, attr)

    def _split_title(self):
        if not self.title:
            self.title = []
            return
        size = reverse_text_len(self.width, self.title_font_size)
        title = self.title.strip()
        self.title = []
        while len(title) > size:
            title_part = title[:size]
            i = title_part.rfind(' ')
            if i == -1:
                i = len(title_part)
            self.title.append(title_part[:i])
            title = title[i:].strip()
        self.title.append(title)

    @property
    def _format(self):
        """Return the value formatter for this graph"""
        return humanize if self.human_readable else str

    def _compute(self):
        """Initial computations to draw the graph"""

    def _plot(self):
        """Actual plotting of the graph"""

    def _compute_margin(self):
        """Compute graph margins from set texts"""
        if self.show_legend and self.series:
            h, w = get_texts_box(
                map(lambda x: truncate(x, self.truncate_legend or 15),
                    cut(self.series, 'title')),
                self.legend_font_size)
            if self.legend_at_bottom:
                h_max = max(h, self.legend_box_size)
                self.margin.bottom += 10 + h_max * round(
                    sqrt(self._order) - 1) * 1.5 + h_max
            else:
                self.margin.right += 10 + w + self.legend_box_size

        if self.title:
            h, _ = get_text_box(self.title[0], self.title_font_size)
            self.margin.top += len(self.title) * (10 + h)

        if self._x_labels:
            h, w = get_texts_box(
                cut(self._x_labels), self.label_font_size)
            self._x_labels_height = 10 + max(
                w * sin(rad(self.x_label_rotation)), h)
            self.margin.bottom += self._x_labels_height
            if self.x_label_rotation:
                self.margin.right = max(
                    w * cos(rad(self.x_label_rotation)),
                    self.margin.right)
        else:
            self._x_labels_height = 0
        if self._y_labels:
            h, w = get_texts_box(
                cut(self._y_labels), self.label_font_size)
            self.margin.left += 10 + max(
                w * cos(rad(self.y_label_rotation)), h)

    @cached_property
    def _legends(self):
        """Getter for series title"""
        return [serie.title for serie in self.series]

    @cached_property
    def _values(self):
        """Getter for series values (flattened)"""
        return [val
                for serie in self.series
                for val in serie.values
                if val is not None]

    @cached_property
    def _len(self):
        """Getter for the maximum series size"""
        return max([len(serie.values) for serie in self.series] or [0])

    @cached_property
    def _min(self):
        """Getter for the minimum series value"""
        return (self.range and self.range[0]) or (
            min(self._values) if self._values else None)

    @cached_property
    def _max(self):
        """Getter for the maximum series value"""
        return (self.range and self.range[1]) or (
            max(self._values) if self._values else None)

    @cached_property
    def _order(self):
        """Getter for the maximum series value"""
        return len(self.series)

    def _draw(self):
        """Draw all the things"""
        self._compute()
        self._split_title()
        self._compute_margin()
        self._decorate()
        if self.series and self._has_data():
            self._plot()
        else:
            self.svg.draw_no_data()

    def _has_data(self):
        """Check if there is any data"""
        return sum(
            map(len, map(lambda s: s.safe_values, self.series))) != 0 and (
                sum(map(abs, self._values)) != 0)

    def render(self, is_unicode):
        """Render the graph, and return the svg string"""
        return self.svg.render(
            is_unicode=is_unicode, pretty_print=self.pretty_print)

    def render_tree(self):
        """Render the graph, and return lxml tree"""
        return self.svg.root
예제 #22
0
class BaseGraph(object):
    """Graphs commons"""

    _adapters = []

    def __init__(self, config, series):
        """Init the graph"""
        self.config = config
        self.series = series or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self.nodes = {}
        self.margin = Margin(*([20] * 4))
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            self.zero = min(
                filter(lambda x: x > 0, [
                    val for serie in self.series for val in serie.safe_values
                ]))
        self._draw()
        self.svg.pre_render()

    def __getattr__(self, attr):
        """Search in config, then in self"""
        if attr in dir(self.config):
            return object.__getattribute__(self.config, attr)
        return object.__getattribute__(self, attr)

    def _split_title(self):
        if not self.title:
            self.title = []
            return
        size = reverse_text_len(self.width, self.title_font_size)
        title = self.title.strip()
        self.title = []
        while len(title) > size:
            title_part = title[:size]
            i = title_part.rfind(' ')
            if i == -1:
                i = len(title_part)
            self.title.append(title_part[:i])
            title = title[i:].strip()
        self.title.append(title)

    @property
    def _format(self):
        """Return the value formatter for this graph"""
        return humanize if self.human_readable else str

    def _compute(self):
        """Initial computations to draw the graph"""

    def _plot(self):
        """Actual plotting of the graph"""

    def _compute_margin(self):
        """Compute graph margins from set texts"""
        if self.show_legend and self.series:
            h, w = get_texts_box(
                map(lambda x: truncate(x, self.truncate_legend or 15),
                    cut(self.series, 'title')), self.legend_font_size)
            if self.legend_at_bottom:
                h_max = max(h, self.legend_box_size)
                self.margin.bottom += 10 + h_max * round(
                    sqrt(self._order) - 1) * 1.5 + h_max
            else:
                self.margin.right += 10 + w + self.legend_box_size

        if self.title:
            h, _ = get_text_box(self.title[0], self.title_font_size)
            self.margin.top += len(self.title) * (10 + h)

        if self._x_labels:
            h, w = get_texts_box(cut(self._x_labels), self.label_font_size)
            self._x_labels_height = 10 + max(
                w * sin(rad(self.x_label_rotation)), h)
            self.margin.bottom += self._x_labels_height
            if self.x_label_rotation:
                self.margin.right = max(w * cos(rad(self.x_label_rotation)),
                                        self.margin.right)
        else:
            self._x_labels_height = 0
        if self._y_labels:
            h, w = get_texts_box(cut(self._y_labels), self.label_font_size)
            self.margin.left += 10 + max(w * cos(rad(self.y_label_rotation)),
                                         h)

    @cached_property
    def _legends(self):
        """Getter for series title"""
        return [serie.title for serie in self.series]

    @cached_property
    def _values(self):
        """Getter for series values (flattened)"""
        return [
            val for serie in self.series for val in serie.values
            if val is not None
        ]

    @cached_property
    def _len(self):
        """Getter for the maximum series size"""
        return max([len(serie.values) for serie in self.series] or [0])

    @cached_property
    def _min(self):
        """Getter for the minimum series value"""
        return (self.range and self.range[0]) or (min(self._values)
                                                  if self._values else None)

    @cached_property
    def _max(self):
        """Getter for the maximum series value"""
        return (self.range and self.range[1]) or (max(self._values)
                                                  if self._values else None)

    @cached_property
    def _order(self):
        """Getter for the maximum series value"""
        return len(self.series)

    def _draw(self):
        """Draw all the things"""
        self._compute()
        self._split_title()
        self._compute_margin()
        self._decorate()
        if self.series and self._has_data():
            self._plot()
        else:
            self.svg.draw_no_data()

    def _has_data(self):
        """Check if there is any data"""
        return sum(map(len, map(lambda s: s.safe_values,
                                self.series))) != 0 and (sum(
                                    map(abs, self._values)) != 0)

    def render(self, is_unicode):
        """Render the graph, and return the svg string"""
        return self.svg.render(is_unicode=is_unicode,
                               pretty_print=self.pretty_print)

    def render_tree(self):
        """Render the graph, and return lxml tree"""
        return self.svg.root
예제 #23
0
class BaseGraph(object):
    """Graphs commons"""

    _adapters = []

    def __init__(self, config, series, secondary_series, uuid, xml_filters):
        """Init the graph"""
        self.uuid = uuid
        self.__dict__.update(config.to_dict())
        self.config = config
        self.series = series or []
        self.secondary_series = secondary_series or []
        self.xml_filters = xml_filters or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin = Margin(self.margin_top or self.margin,
                             self.margin_right or self.margin,
                             self.margin_bottom or self.margin,
                             self.margin_left or self.margin)
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            if self._dual:
                get = lambda x: x[1] or 1
            else:
                get = lambda x: x

            positive_values = list(filter(
                lambda x: x > 0,
                [get(val)
                 for serie in self.series for val in serie.safe_values]))

            self.zero = min(positive_values or (1,)) or 1
        if self._len < 3:
            self.interpolate = None
        self._draw()
        self.svg.pre_render()

    @property
    def all_series(self):
        return self.series + self.secondary_series

    @property
    def _x_format(self):
        """Return the value formatter for this graph"""
        return self.config.x_value_formatter or (
            humanize if self.human_readable else str)

    @property
    def _format(self):
        """Return the value formatter for this graph"""
        return self.config.value_formatter or (
            humanize if self.human_readable else str)

    def _compute(self):
        """Initial computations to draw the graph"""

    def _compute_margin(self):
        """Compute graph margins from set texts"""
        self._legend_at_left_width = 0
        for series_group in (self.series, self.secondary_series):
            if self.show_legend and series_group:
                h, w = get_texts_box(
                    map(lambda x: truncate(x, self.truncate_legend or 15),
                        cut(series_group, 'title')),
                    self.legend_font_size)
                if self.legend_at_bottom:
                    h_max = max(h, self.legend_box_size)
                    cols = (self._order // self.legend_at_bottom_columns
                            if self.legend_at_bottom_columns
                            else ceil(sqrt(self._order)) or 1)
                    self.margin.bottom += self.spacing + h_max * round(
                        cols - 1) * 1.5 + h_max
                else:
                    if series_group is self.series:
                        legend_width = self.spacing + w + self.legend_box_size
                        self.margin.left += legend_width
                        self._legend_at_left_width += legend_width
                    else:
                        self.margin.right += (
                            self.spacing + w + self.legend_box_size)

        self._x_labels_height = 0
        if (self._x_labels or self._x_2nd_labels) and self.show_x_labels:
            for xlabels in (self._x_labels, self._x_2nd_labels):
                if xlabels:
                    h, w = get_texts_box(
                        map(lambda x: truncate(x, self.truncate_label or 25),
                            cut(xlabels)),
                        self.label_font_size)
                    self._x_labels_height = self.spacing + max(
                        w * sin(rad(self.x_label_rotation)), h)
                    if xlabels is self._x_labels:
                        self.margin.bottom += self._x_labels_height
                    else:
                        self.margin.top += self._x_labels_height
                    if self.x_label_rotation:
                        self.margin.right = max(
                            w * cos(rad(self.x_label_rotation)),
                            self.margin.right)

        if self.show_y_labels:
            for ylabels in (self._y_labels, self._y_2nd_labels):
                if ylabels:
                    h, w = get_texts_box(
                        cut(ylabels), self.label_font_size)
                    if ylabels is self._y_labels:
                        self.margin.left += self.spacing + max(
                            w * cos(rad(self.y_label_rotation)), h)
                    else:
                        self.margin.right += self.spacing + max(
                            w * cos(rad(self.y_label_rotation)), h)

        self.title = split_title(
            self.title, self.width, self.title_font_size)

        if self.title:
            h, _ = get_text_box(self.title[0], self.title_font_size)
            self.margin.top += len(self.title) * (self.spacing + h)

        self.x_title = split_title(
            self.x_title, self.width - self.margin.x, self.title_font_size)

        self._x_title_height = 0
        if self.x_title:
            h, _ = get_text_box(self.x_title[0], self.title_font_size)
            height = len(self.x_title) * (self.spacing + h)
            self.margin.bottom += height
            self._x_title_height = height + self.spacing

        self.y_title = split_title(
            self.y_title, self.height - self.margin.y, self.title_font_size)

        self._y_title_height = 0
        if self.y_title:
            h, _ = get_text_box(self.y_title[0], self.title_font_size)
            height = len(self.y_title) * (self.spacing + h)
            self.margin.left += height
            self._y_title_height = height + self.spacing

    @cached_property
    def _legends(self):
        """Getter for series title"""
        return [serie.title for serie in self.series]

    @cached_property
    def _secondary_legends(self):
        """Getter for series title on secondary y axis"""
        return [serie.title for serie in self.secondary_series]

    @cached_property
    def _values(self):
        """Getter for series values (flattened)"""
        return [val
                for serie in self.series
                for val in serie.values
                if val is not None]

    @cached_property
    def _secondary_values(self):
        """Getter for secondary series values (flattened)"""
        return [val
                for serie in self.secondary_series
                for val in serie.values
                if val is not None]

    @cached_property
    def _len(self):
        """Getter for the maximum series size"""
        return max([
            len(serie.values)
            for serie in self.all_series] or [0])

    @cached_property
    def _secondary_min(self):
        """Getter for the minimum series value"""
        return (self.range[0] if (self.range and self.range[0] is not None)
                else (min(self._secondary_values)
                      if self._secondary_values else None))

    @cached_property
    def _min(self):
        """Getter for the minimum series value"""
        return (self.range[0] if (self.range and self.range[0] is not None)
                else (min(self._values)
                      if self._values else None))

    @cached_property
    def _max(self):
        """Getter for the maximum series value"""
        return (self.range[1] if (self.range and self.range[1] is not None)
                else (max(self._values) if self._values else None))

    @cached_property
    def _secondary_max(self):
        """Getter for the maximum series value"""
        return (self.range[1] if (self.range and self.range[1] is not None)
                else (max(self._secondary_values)
                      if self._secondary_values else None))

    @cached_property
    def _order(self):
        """Getter for the number of series"""
        return len(self.all_series)

    @cached_property
    def _x_major_labels(self):
        """Getter for the x major label"""
        if self.x_labels_major:
            return self.x_labels_major
        if self.x_labels_major_every:
            return [self._x_labels[i][0] for i in range(
                0, len(self._x_labels), self.x_labels_major_every)]
        if self.x_labels_major_count:
            label_count = len(self._x_labels)
            major_count = self.x_labels_major_count
            if (major_count >= label_count):
                return [label[0] for label in self._x_labels]

            return [self._x_labels[
                    int(i * (label_count - 1) / (major_count - 1))][0]
                    for i in range(major_count)]

        return []

    @cached_property
    def _y_major_labels(self):
        """Getter for the y major label"""
        if self.y_labels_major:
            return self.y_labels_major
        if self.y_labels_major_every:
            return [self._y_labels[i][1] for i in range(
                0, len(self._y_labels), self.y_labels_major_every)]
        if self.y_labels_major_count:
            label_count = len(self._y_labels)
            major_count = self.y_labels_major_count
            if (major_count >= label_count):
                return [label[1] for label in self._y_labels]

            return [self._y_labels[
                int(i * (label_count - 1) / (major_count - 1))][1]
                for i in range(major_count)]

        return majorize(
            cut(self._y_labels, 1)
        )

    def _draw(self):
        """Draw all the things"""
        self._compute()
        self._compute_secondary()
        self._post_compute()
        self._compute_margin()
        self._decorate()
        if self.series and self._has_data():
            self._plot()
        else:
            self.svg.draw_no_data()

    def _has_data(self):
        """Check if there is any data"""
        return sum(
            map(len, map(lambda s: s.safe_values, self.series))) != 0 and (
            sum(map(abs, self._values)) != 0)

    def render(self, is_unicode=False):
        """Render the graph, and return the svg string"""
        return self.svg.render(
            is_unicode=is_unicode, pretty_print=self.pretty_print)

    def render_tree(self):
        """Render the graph, and return (l)xml etree"""
        svg = self.svg.root
        for f in self.xml_filters:
            svg = f(svg)
        return svg
예제 #24
0
파일: base.py 프로젝트: signed0/pygal
class BaseGraph(object):
    """Graphs commons"""

    _adapters = []

    def __init__(self, config, series, secondary_series, uuid):
        """Init the graph"""
        self.uuid = uuid
        self.config = config
        self.series = series or []
        self.secondary_series = secondary_series or []
        self.horizontal = getattr(self, 'horizontal', False)
        self.svg = Svg(self)
        self._x_labels = None
        self._y_labels = None
        self._x_2nd_labels = None
        self._y_2nd_labels = None
        self.nodes = {}
        self.margin = Margin(*([self.margin] * 4))
        self._box = Box()
        self.view = None
        if self.logarithmic and self.zero == 0:
            # Explicit min to avoid interpolation dependency
            from pygal.graph.xy import XY
            if isinstance(self, XY):
                get = lambda x: x[1]
            else:
                get = lambda x: x

            positive_values = list(
                filter(lambda x: x > 0, [
                    get(val) for serie in self.series
                    for val in serie.safe_values
                ]))

            self.zero = min(positive_values) if positive_values else 0
        self._draw()
        self.svg.pre_render()

    def __getattr__(self, attr):
        """Search in config, then in self"""
        if attr in dir(self.config):
            return object.__getattribute__(self.config, attr)
        return object.__getattribute__(self, attr)

    @property
    def all_series(self):
        return self.series + self.secondary_series

    @property
    def _format(self):
        """Return the value formatter for this graph"""
        return self.value_formatter or (humanize
                                        if self.human_readable else str)

    def _compute(self):
        """Initial computations to draw the graph"""

    def _plot(self):
        """Actual plotting of the graph"""

    def _compute_margin(self):
        """Compute graph margins from set texts"""
        self._legend_at_left_width = 0
        for series_group in (self.series, self.secondary_series):
            if self.show_legend and series_group:
                h, w = get_texts_box(
                    map(lambda x: truncate(x, self.truncate_legend or 15),
                        cut(series_group, 'title')), self.legend_font_size)
                if self.legend_at_bottom:
                    h_max = max(h, self.legend_box_size)
                    self.margin.bottom += self.spacing + h_max * round(
                        sqrt(self._order) - 1) * 1.5 + h_max
                else:
                    if series_group is self.series:
                        legend_width = self.spacing + w + self.legend_box_size
                        self.margin.left += legend_width
                        self._legend_at_left_width += legend_width
                    else:
                        self.margin.right += (self.spacing + w +
                                              self.legend_box_size)

        for xlabels in (self._x_labels, self._x_2nd_labels):
            if xlabels:
                h, w = get_texts_box(
                    map(lambda x: truncate(x, self.truncate_label or 25),
                        cut(xlabels)), self.label_font_size)
                self._x_labels_height = self.spacing + max(
                    w * sin(rad(self.x_label_rotation)), h)
                if xlabels is self._x_labels:
                    self.margin.bottom += self._x_labels_height
                else:
                    self.margin.top += self._x_labels_height
                if self.x_label_rotation:
                    self.margin.right = max(
                        w * cos(rad(self.x_label_rotation)), self.margin.right)
        if not self._x_labels:
            self._x_labels_height = 0

        if self.show_y_labels:
            for ylabels in (self._y_labels, self._y_2nd_labels):
                if ylabels:
                    h, w = get_texts_box(cut(ylabels), self.label_font_size)
                    if ylabels is self._y_labels:
                        self.margin.left += self.spacing + max(
                            w * cos(rad(self.y_label_rotation)), h)
                    else:
                        self.margin.right += self.spacing + max(
                            w * cos(rad(self.y_label_rotation)), h)

        self.title = split_title(self.title, self.width, self.title_font_size)

        if self.title:
            h, _ = get_text_box(self.title[0], self.title_font_size)
            self.margin.top += len(self.title) * (self.spacing + h)

        self.x_title = split_title(self.x_title, self.width - self.margin.x,
                                   self.title_font_size)

        self._x_title_height = 0
        if self.x_title:
            h, _ = get_text_box(self.x_title[0], self.title_font_size)
            height = len(self.x_title) * (self.spacing + h)
            self.margin.bottom += height
            self._x_title_height = height + self.spacing

        self.y_title = split_title(self.y_title, self.height - self.margin.y,
                                   self.title_font_size)

        self._y_title_height = 0
        if self.y_title:
            h, _ = get_text_box(self.y_title[0], self.title_font_size)
            height = len(self.y_title) * (self.spacing + h)
            self.margin.left += height
            self._y_title_height = height + self.spacing

    @cached_property
    def _legends(self):
        """Getter for series title"""
        return [serie.title for serie in self.series]

    @cached_property
    def _secondary_legends(self):
        """Getter for series title on secondary y axis"""
        return [serie.title for serie in self.secondary_series]

    @cached_property
    def _values(self):
        """Getter for series values (flattened)"""
        return [
            val for serie in self.series for val in serie.values
            if val is not None
        ]

    @cached_property
    def _secondary_values(self):
        """Getter for secondary series values (flattened)"""
        return [
            val for serie in self.secondary_series for val in serie.values
            if val is not None
        ]

    @cached_property
    def _len(self):
        """Getter for the maximum series size"""
        return max([len(serie.values) for serie in self.all_series] or [0])

    @cached_property
    def _secondary_min(self):
        """Getter for the minimum series value"""
        return (self.range and self.range[0]) or (min(
            self._secondary_values) if self._secondary_values else None)

    @cached_property
    def _min(self):
        """Getter for the minimum series value"""
        return (self.range and self.range[0]) or (min(self._values)
                                                  if self._values else None)

    @cached_property
    def _max(self):
        """Getter for the maximum series value"""
        return (self.range and self.range[1]) or (max(self._values)
                                                  if self._values else None)

    @cached_property
    def _secondary_max(self):
        """Getter for the maximum series value"""
        return (self.range and self.range[1]) or (max(
            self._secondary_values) if self._secondary_values else None)

    @cached_property
    def _order(self):
        """Getter for the number of series"""
        return len(self.all_series)

    def _draw(self):
        """Draw all the things"""
        self._compute()
        self._compute_secondary()
        self._post_compute()
        self._compute_margin()
        self._decorate()
        if self.series and self._has_data():
            self._plot()
        else:
            self.svg.draw_no_data()

    def _has_data(self):
        """Check if there is any data"""
        return sum(map(len, map(lambda s: s.safe_values,
                                self.series))) != 0 and (sum(
                                    map(abs, self._values)) != 0)

    def render(self, is_unicode=False):
        """Render the graph, and return the svg string"""
        return self.svg.render(is_unicode=is_unicode,
                               pretty_print=self.pretty_print)

    def render_tree(self):
        """Render the graph, and return lxml tree"""
        return self.svg.root