Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/xlsxwriter/chart_pie.py: 16%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

94 statements  

1############################################################################### 

2# 

3# ChartPie - A class for writing the Excel XLSX Pie charts. 

4# 

5# SPDX-License-Identifier: BSD-2-Clause 

6# Copyright 2013-2024, John McNamara, jmcnamara@cpan.org 

7# 

8 

9from warnings import warn 

10from . import chart 

11 

12 

13class ChartPie(chart.Chart): 

14 """ 

15 A class for writing the Excel XLSX Pie charts. 

16 

17 

18 """ 

19 

20 ########################################################################### 

21 # 

22 # Public API. 

23 # 

24 ########################################################################### 

25 

26 def __init__(self, options=None): 

27 """ 

28 Constructor. 

29 

30 """ 

31 super(ChartPie, self).__init__() 

32 

33 self.vary_data_color = 1 

34 self.rotation = 0 

35 

36 # Set the available data label positions for this chart type. 

37 self.label_position_default = "best_fit" 

38 self.label_positions = { 

39 "center": "ctr", 

40 "inside_end": "inEnd", 

41 "outside_end": "outEnd", 

42 "best_fit": "bestFit", 

43 } 

44 

45 def set_rotation(self, rotation): 

46 """ 

47 Set the Pie/Doughnut chart rotation: the angle of the first slice. 

48 

49 Args: 

50 rotation: First segment angle: 0 <= rotation <= 360. 

51 

52 Returns: 

53 Nothing. 

54 

55 """ 

56 if rotation is None: 

57 return 

58 

59 # Ensure the rotation is in Excel's range. 

60 if rotation < 0 or rotation > 360: 

61 warn( 

62 "Chart rotation %d outside Excel range: 0 <= rotation <= 360" % rotation 

63 ) 

64 return 

65 

66 self.rotation = int(rotation) 

67 

68 ########################################################################### 

69 # 

70 # Private API. 

71 # 

72 ########################################################################### 

73 

74 def _write_chart_type(self, args): 

75 # Override the virtual superclass method with a chart specific method. 

76 # Write the c:pieChart element. 

77 self._write_pie_chart(args) 

78 

79 ########################################################################### 

80 # 

81 # XML methods. 

82 # 

83 ########################################################################### 

84 

85 def _write_pie_chart(self, args): 

86 # Write the <c:pieChart> element. Over-ridden method to remove 

87 # axis_id code since Pie charts don't require val and cat axes. 

88 self._xml_start_tag("c:pieChart") 

89 

90 # Write the c:varyColors element. 

91 self._write_vary_colors() 

92 

93 # Write the series elements. 

94 for data in self.series: 

95 self._write_ser(data) 

96 

97 # Write the c:firstSliceAng element. 

98 self._write_first_slice_ang() 

99 

100 self._xml_end_tag("c:pieChart") 

101 

102 def _write_plot_area(self): 

103 # Over-ridden method to remove the cat_axis() and val_axis() code 

104 # since Pie charts don't require those axes. 

105 # 

106 # Write the <c:plotArea> element. 

107 

108 self._xml_start_tag("c:plotArea") 

109 

110 # Write the c:layout element. 

111 self._write_layout(self.plotarea.get("layout"), "plot") 

112 

113 # Write the subclass chart type element. 

114 self._write_chart_type(None) 

115 # Configure a combined chart if present. 

116 second_chart = self.combined 

117 

118 if second_chart: 

119 # Secondary axis has unique id otherwise use same as primary. 

120 if second_chart.is_secondary: 

121 second_chart.id = 1000 + self.id 

122 else: 

123 second_chart.id = self.id 

124 

125 # Share the same filehandle for writing. 

126 second_chart.fh = self.fh 

127 

128 # Share series index with primary chart. 

129 second_chart.series_index = self.series_index 

130 

131 # Write the subclass chart type elements for combined chart. 

132 second_chart._write_chart_type(None) 

133 

134 # Write the c:spPr element for the plotarea formatting. 

135 self._write_sp_pr(self.plotarea) 

136 

137 self._xml_end_tag("c:plotArea") 

138 

139 def _write_legend(self): 

140 # Over-ridden method to add <c:txPr> to legend. 

141 # Write the <c:legend> element. 

142 legend = self.legend 

143 position = legend.get("position", "right") 

144 font = legend.get("font") 

145 delete_series = [] 

146 overlay = 0 

147 

148 if legend.get("delete_series") and isinstance(legend["delete_series"], list): 

149 delete_series = legend["delete_series"] 

150 

151 if position.startswith("overlay_"): 

152 position = position.replace("overlay_", "") 

153 overlay = 1 

154 

155 allowed = { 

156 "right": "r", 

157 "left": "l", 

158 "top": "t", 

159 "bottom": "b", 

160 "top_right": "tr", 

161 } 

162 

163 if position == "none": 

164 return 

165 

166 if position not in allowed: 

167 return 

168 

169 position = allowed[position] 

170 

171 self._xml_start_tag("c:legend") 

172 

173 # Write the c:legendPos element. 

174 self._write_legend_pos(position) 

175 

176 # Remove series labels from the legend. 

177 for index in delete_series: 

178 # Write the c:legendEntry element. 

179 self._write_legend_entry(index) 

180 

181 # Write the c:layout element. 

182 self._write_layout(legend.get("layout"), "legend") 

183 

184 # Write the c:overlay element. 

185 if overlay: 

186 self._write_overlay() 

187 

188 # Write the c:spPr element. 

189 self._write_sp_pr(legend) 

190 

191 # Write the c:txPr element. Over-ridden. 

192 self._write_tx_pr_legend(None, font) 

193 

194 self._xml_end_tag("c:legend") 

195 

196 def _write_tx_pr_legend(self, horiz, font): 

197 # Write the <c:txPr> element for legends. 

198 

199 if font and font.get("rotation"): 

200 rotation = font["rotation"] 

201 else: 

202 rotation = None 

203 

204 self._xml_start_tag("c:txPr") 

205 

206 # Write the a:bodyPr element. 

207 self._write_a_body_pr(rotation, horiz) 

208 

209 # Write the a:lstStyle element. 

210 self._write_a_lst_style() 

211 

212 # Write the a:p element. 

213 self._write_a_p_legend(font) 

214 

215 self._xml_end_tag("c:txPr") 

216 

217 def _write_a_p_legend(self, font): 

218 # Write the <a:p> element for legends. 

219 

220 self._xml_start_tag("a:p") 

221 

222 # Write the a:pPr element. 

223 self._write_a_p_pr_legend(font) 

224 

225 # Write the a:endParaRPr element. 

226 self._write_a_end_para_rpr() 

227 

228 self._xml_end_tag("a:p") 

229 

230 def _write_a_p_pr_legend(self, font): 

231 # Write the <a:pPr> element for legends. 

232 attributes = [("rtl", 0)] 

233 

234 self._xml_start_tag("a:pPr", attributes) 

235 

236 # Write the a:defRPr element. 

237 self._write_a_def_rpr(font) 

238 

239 self._xml_end_tag("a:pPr") 

240 

241 def _write_vary_colors(self): 

242 # Write the <c:varyColors> element. 

243 attributes = [("val", 1)] 

244 

245 self._xml_empty_tag("c:varyColors", attributes) 

246 

247 def _write_first_slice_ang(self): 

248 # Write the <c:firstSliceAng> element. 

249 attributes = [("val", self.rotation)] 

250 

251 self._xml_empty_tag("c:firstSliceAng", attributes) 

252 

253 def _write_show_leader_lines(self): 

254 # Write the <c:showLeaderLines> element. 

255 # 

256 # This is for Pie/Doughnut charts. Other chart types only supported 

257 # leader lines after Excel 2015 via an extension element. 

258 attributes = [("val", 1)] 

259 

260 self._xml_empty_tag("c:showLeaderLines", attributes)