Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Copyright (c) 2010-2020 openpyxl 

2 

3from collections import OrderedDict 

4 

5from openpyxl.descriptors import ( 

6 Typed, 

7 Integer, 

8 Alias, 

9 MinMax, 

10 Bool, 

11 Set, 

12) 

13from openpyxl.descriptors.sequence import ValueSequence 

14from openpyxl.descriptors.serialisable import Serialisable 

15 

16from ._3d import _3DBase 

17from .data_source import AxDataSource, NumRef 

18from .layout import Layout 

19from .legend import Legend 

20from .reference import Reference 

21from .series_factory import SeriesFactory 

22from .series import attribute_mapping 

23from .shapes import GraphicalProperties 

24from .title import TitleDescriptor 

25 

26class AxId(Serialisable): 

27 

28 val = Integer() 

29 

30 def __init__(self, val): 

31 self.val = val 

32 

33 

34def PlotArea(): 

35 from .chartspace import PlotArea 

36 return PlotArea() 

37 

38 

39class ChartBase(Serialisable): 

40 

41 """ 

42 Base class for all charts 

43 """ 

44 

45 legend = Typed(expected_type=Legend, allow_none=True) 

46 layout = Typed(expected_type=Layout, allow_none=True) 

47 roundedCorners = Bool(allow_none=True) 

48 axId = ValueSequence(expected_type=int) 

49 visible_cells_only = Bool(allow_none=True) 

50 display_blanks = Set(values=['span', 'gap', 'zero']) 

51 

52 _series_type = "" 

53 ser = () 

54 series = Alias('ser') 

55 title = TitleDescriptor() 

56 anchor = "E15" # default anchor position 

57 width = 15 # in cm, approx 5 rows 

58 height = 7.5 # in cm, approx 14 rows 

59 _id = 1 

60 _path = "/xl/charts/chart{0}.xml" 

61 style = MinMax(allow_none=True, min=1, max=48) 

62 mime_type = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml" 

63 graphical_properties = Typed(expected_type=GraphicalProperties, allow_none=True) 

64 

65 __elements__ = () 

66 

67 

68 def __init__(self, axId=(), **kw): 

69 self._charts = [self] 

70 self.title = None 

71 self.layout = None 

72 self.roundedCorners = None 

73 self.legend = Legend() 

74 self.graphical_properties = None 

75 self.style = None 

76 self.plot_area = PlotArea() 

77 self.axId = axId 

78 self.display_blanks = 'gap' 

79 self.pivotSource = None 

80 self.pivotFormats = () 

81 self.visible_cells_only = True 

82 self.idx_base = 0 

83 super(ChartBase, self).__init__() 

84 

85 

86 def __hash__(self): 

87 """ 

88 Just need to check for identity 

89 """ 

90 return id(self) 

91 

92 def __iadd__(self, other): 

93 """ 

94 Combine the chart with another one 

95 """ 

96 if not isinstance(other, ChartBase): 

97 raise TypeError("Only other charts can be added") 

98 self._charts.append(other) 

99 return self 

100 

101 

102 def to_tree(self, namespace=None, tagname=None, idx=None): 

103 self.axId = [id for id in self._axes] 

104 if self.ser is not None: 

105 for s in self.ser: 

106 s.__elements__ = attribute_mapping[self._series_type] 

107 return super(ChartBase, self).to_tree(tagname, idx) 

108 

109 

110 def _write(self): 

111 from .chartspace import ChartSpace, ChartContainer 

112 self.plot_area.layout = self.layout 

113 

114 idx_base = self.idx_base 

115 for chart in self._charts: 

116 if chart not in self.plot_area._charts: 

117 chart.idx_base = idx_base 

118 idx_base += len(chart.series) 

119 self.plot_area._charts = self._charts 

120 

121 container = ChartContainer(plotArea=self.plot_area, legend=self.legend, title=self.title) 

122 if isinstance(chart, _3DBase): 

123 container.view3D = chart.view3D 

124 container.floor = chart.floor 

125 container.sideWall = chart.sideWall 

126 container.backWall = chart.backWall 

127 container.plotVisOnly = self.visible_cells_only 

128 container.dispBlanksAs = self.display_blanks 

129 container.pivotFmts = self.pivotFormats 

130 cs = ChartSpace(chart=container) 

131 cs.style = self.style 

132 cs.roundedCorners = self.roundedCorners 

133 cs.pivotSource = self.pivotSource 

134 return cs.to_tree() 

135 

136 

137 @property 

138 def _axes(self): 

139 x = getattr(self, "x_axis", None) 

140 y = getattr(self, "y_axis", None) 

141 z = getattr(self, "z_axis", None) 

142 return OrderedDict([(axis.axId, axis) for axis in (x, y, z) if axis]) 

143 

144 

145 def set_categories(self, labels): 

146 """ 

147 Set the categories / x-axis values 

148 """ 

149 if not isinstance(labels, Reference): 

150 labels = Reference(range_string=labels) 

151 for s in self.ser: 

152 s.cat = AxDataSource(numRef=NumRef(f=labels)) 

153 

154 

155 def add_data(self, data, from_rows=False, titles_from_data=False): 

156 """ 

157 Add a range of data in a single pass. 

158 The default is to treat each column as a data series. 

159 """ 

160 if not isinstance(data, Reference): 

161 data = Reference(range_string=data) 

162 

163 if from_rows: 

164 values = data.rows 

165 

166 else: 

167 values = data.cols 

168 

169 for ref in values: 

170 series = SeriesFactory(ref, title_from_data=titles_from_data) 

171 self.series.append(series) 

172 

173 

174 def append(self, value): 

175 """Append a data series to the chart""" 

176 l = self.series[:] 

177 l.append(value) 

178 self.series = l 

179 

180 

181 @property 

182 def path(self): 

183 return self._path.format(self._id)