mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-04-22 02:23:48 +00:00
197 lines
5.5 KiB
Python
197 lines
5.5 KiB
Python
# Copyright (c) 2010-2022 openpyxl
|
|
|
|
from collections import OrderedDict
|
|
from operator import attrgetter
|
|
|
|
from openpyxl.descriptors import (
|
|
Typed,
|
|
Integer,
|
|
Alias,
|
|
MinMax,
|
|
Bool,
|
|
Set,
|
|
)
|
|
from openpyxl.descriptors.sequence import ValueSequence
|
|
from openpyxl.descriptors.serialisable import Serialisable
|
|
|
|
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 _reindex(self):
|
|
"""
|
|
Normalise and rebase series: sort by order and then rebase order
|
|
|
|
"""
|
|
# sort data series in order and rebase
|
|
ds = sorted(self.series, key=attrgetter("order"))
|
|
for idx, s in enumerate(ds):
|
|
s.order = idx
|
|
self.series = ds
|
|
|
|
|
|
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)
|