123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- from __future__ import absolute_import
- # Copyright (c) 2010-2019 openpyxl
- from collections import OrderedDict
- from openpyxl.compat import basestring
- from openpyxl.descriptors import (
- Typed,
- Integer,
- Alias,
- MinMax,
- Bool,
- Set,
- )
- from openpyxl.descriptors.nested import Nested
- from openpyxl.descriptors.sequence import NestedSequence, ValueSequence
- from openpyxl.descriptors.serialisable import Serialisable
- from openpyxl.xml.constants import PACKAGE_CHARTS
- from ._3d import _3DBase
- from .data_source import AxDataSource, NumRef
- from .layout import Layout
- from .legend import Legend
- from .reference import Reference
- from .series_factory import SeriesFactory
- from .series import attribute_mapping
- from .shapes import GraphicalProperties
- from .title import TitleDescriptor
- class AxId(Serialisable):
- val = Integer()
- def __init__(self, val):
- self.val = val
- def PlotArea():
- from .chartspace import PlotArea
- return PlotArea()
- class ChartBase(Serialisable):
- """
- Base class for all charts
- """
- legend = Typed(expected_type=Legend, allow_none=True)
- layout = Typed(expected_type=Layout, allow_none=True)
- roundedCorners = Bool(allow_none=True)
- axId = ValueSequence(expected_type=int)
- visible_cells_only = Bool(allow_none=True)
- display_blanks = Set(values=['span', 'gap', 'zero'])
- _series_type = ""
- ser = ()
- series = Alias('ser')
- title = TitleDescriptor()
- anchor = "E15" # default anchor position
- width = 15 # in cm, approx 5 rows
- height = 7.5 # in cm, approx 14 rows
- _id = 1
- _path = "/xl/charts/chart{0}.xml"
- style = MinMax(allow_none=True, min=1, max=48)
- mime_type = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
- graphical_properties = Typed(expected_type=GraphicalProperties, allow_none=True)
- __elements__ = ()
- def __init__(self, axId=(), **kw):
- self._charts = [self]
- self.title = None
- self.layout = None
- self.roundedCorners = None
- self.legend = Legend()
- self.graphical_properties = None
- self.style = None
- self.plot_area = PlotArea()
- self.axId = axId
- self.display_blanks = 'gap'
- self.pivotSource = None
- self.pivotFormats = ()
- self.visible_cells_only = True
- self.idx_base = 0
- super(ChartBase, self).__init__()
- def __hash__(self):
- """
- Just need to check for identity
- """
- return id(self)
- def __iadd__(self, other):
- """
- Combine the chart with another one
- """
- if not isinstance(other, ChartBase):
- raise TypeError("Only other charts can be added")
- self._charts.append(other)
- return self
- def to_tree(self, namespace=None, tagname=None, idx=None):
- self.axId = [id for id in self._axes]
- if self.ser is not None:
- for s in self.ser:
- s.__elements__ = attribute_mapping[self._series_type]
- return super(ChartBase, self).to_tree(tagname, idx)
- def _write(self):
- from .chartspace import ChartSpace, ChartContainer
- self.plot_area.layout = self.layout
- idx_base = self.idx_base
- for chart in self._charts:
- if chart not in self.plot_area._charts:
- chart.idx_base = idx_base
- idx_base += len(chart.series)
- self.plot_area._charts = self._charts
- container = ChartContainer(plotArea=self.plot_area, legend=self.legend, title=self.title)
- if isinstance(chart, _3DBase):
- container.view3D = chart.view3D
- container.floor = chart.floor
- container.sideWall = chart.sideWall
- container.backWall = chart.backWall
- container.plotVisOnly = self.visible_cells_only
- container.dispBlanksAs = self.display_blanks
- container.pivotFmts = self.pivotFormats
- cs = ChartSpace(chart=container)
- cs.style = self.style
- cs.roundedCorners = self.roundedCorners
- cs.pivotSource = self.pivotSource
- return cs.to_tree()
- @property
- def _axes(self):
- x = getattr(self, "x_axis", None)
- y = getattr(self, "y_axis", None)
- z = getattr(self, "z_axis", None)
- return OrderedDict([(axis.axId, axis) for axis in (x, y, z) if axis])
- def set_categories(self, labels):
- """
- Set the categories / x-axis values
- """
- if not isinstance(labels, Reference):
- labels = Reference(range_string=labels)
- for s in self.ser:
- s.cat = AxDataSource(numRef=NumRef(f=labels))
- def add_data(self, data, from_rows=False, titles_from_data=False):
- """
- Add a range of data in a single pass.
- The default is to treat each column as a data series.
- """
- if not isinstance(data, Reference):
- data = Reference(range_string=data)
- if from_rows:
- values = data.rows
- else:
- values = data.cols
- for ref in values:
- series = SeriesFactory(ref, title_from_data=titles_from_data)
- self.series.append(series)
- def append(self, value):
- """Append a data series to the chart"""
- l = self.series[:]
- l.append(value)
- self.series = l
- @property
- def path(self):
- return self._path.format(self._id)
|