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

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

117 statements  

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

2# 

3# ChartScatter - A class for writing the Excel XLSX Scatter charts. 

4# 

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

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

7# 

8 

9from . import chart 

10from warnings import warn 

11 

12 

13class ChartScatter(chart.Chart): 

14 """ 

15 A class for writing the Excel XLSX Scatter 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(ChartScatter, self).__init__() 

32 

33 if options is None: 

34 options = {} 

35 

36 self.subtype = options.get("subtype") 

37 

38 if not self.subtype: 

39 self.subtype = "marker_only" 

40 

41 self.cross_between = "midCat" 

42 self.horiz_val_axis = 0 

43 self.val_axis_position = "b" 

44 self.smooth_allowed = True 

45 self.requires_category = True 

46 

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

48 self.label_position_default = "right" 

49 self.label_positions = { 

50 "center": "ctr", 

51 "right": "r", 

52 "left": "l", 

53 "above": "t", 

54 "below": "b", 

55 # For backward compatibility. 

56 "top": "t", 

57 "bottom": "b", 

58 } 

59 

60 def combine(self, chart=None): 

61 """ 

62 Create a combination chart with a secondary chart. 

63 

64 Note: Override parent method to add a warning. 

65 

66 Args: 

67 chart: The secondary chart to combine with the primary chart. 

68 

69 Returns: 

70 Nothing. 

71 

72 """ 

73 if chart is None: 

74 return 

75 

76 warn( 

77 "Combined chart not currently supported with scatter chart " 

78 "as the primary chart" 

79 ) 

80 

81 ########################################################################### 

82 # 

83 # Private API. 

84 # 

85 ########################################################################### 

86 

87 def _write_chart_type(self, args): 

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

89 # Write the c:scatterChart element. 

90 self._write_scatter_chart(args) 

91 

92 ########################################################################### 

93 # 

94 # XML methods. 

95 # 

96 ########################################################################### 

97 

98 def _write_scatter_chart(self, args): 

99 # Write the <c:scatterChart> element. 

100 

101 if args["primary_axes"]: 

102 series = self._get_primary_axes_series() 

103 else: 

104 series = self._get_secondary_axes_series() 

105 

106 if not len(series): 

107 return 

108 

109 style = "lineMarker" 

110 subtype = self.subtype 

111 

112 # Set the user defined chart subtype. 

113 if subtype == "marker_only": 

114 style = "lineMarker" 

115 

116 if subtype == "straight_with_markers": 

117 style = "lineMarker" 

118 

119 if subtype == "straight": 

120 style = "lineMarker" 

121 self.default_marker = {"type": "none"} 

122 

123 if subtype == "smooth_with_markers": 

124 style = "smoothMarker" 

125 

126 if subtype == "smooth": 

127 style = "smoothMarker" 

128 self.default_marker = {"type": "none"} 

129 

130 # Add default formatting to the series data. 

131 self._modify_series_formatting() 

132 

133 self._xml_start_tag("c:scatterChart") 

134 

135 # Write the c:scatterStyle element. 

136 self._write_scatter_style(style) 

137 

138 # Write the series elements. 

139 for data in series: 

140 self._write_ser(data) 

141 

142 # Write the c:axId elements 

143 self._write_axis_ids(args) 

144 

145 self._xml_end_tag("c:scatterChart") 

146 

147 def _write_ser(self, series): 

148 # Over-ridden to write c:xVal/c:yVal instead of c:cat/c:val elements. 

149 # Write the <c:ser> element. 

150 

151 index = self.series_index 

152 self.series_index += 1 

153 

154 self._xml_start_tag("c:ser") 

155 

156 # Write the c:idx element. 

157 self._write_idx(index) 

158 

159 # Write the c:order element. 

160 self._write_order(index) 

161 

162 # Write the series name. 

163 self._write_series_name(series) 

164 

165 # Write the c:spPr element. 

166 self._write_sp_pr(series) 

167 

168 # Write the c:marker element. 

169 self._write_marker(series.get("marker")) 

170 

171 # Write the c:dPt element. 

172 self._write_d_pt(series.get("points")) 

173 

174 # Write the c:dLbls element. 

175 self._write_d_lbls(series.get("labels")) 

176 

177 # Write the c:trendline element. 

178 self._write_trendline(series.get("trendline")) 

179 

180 # Write the c:errBars element. 

181 self._write_error_bars(series.get("error_bars")) 

182 

183 # Write the c:xVal element. 

184 self._write_x_val(series) 

185 

186 # Write the c:yVal element. 

187 self._write_y_val(series) 

188 

189 # Write the c:smooth element. 

190 if "smooth" in self.subtype and series["smooth"] is None: 

191 # Default is on for smooth scatter charts. 

192 self._write_c_smooth(True) 

193 else: 

194 self._write_c_smooth(series["smooth"]) 

195 

196 self._xml_end_tag("c:ser") 

197 

198 def _write_plot_area(self): 

199 # Over-ridden to have 2 valAx elements for scatter charts instead 

200 # of catAx/valAx. 

201 # 

202 # Write the <c:plotArea> element. 

203 self._xml_start_tag("c:plotArea") 

204 

205 # Write the c:layout element. 

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

207 

208 # Write the subclass chart elements for primary and secondary axes. 

209 self._write_chart_type({"primary_axes": 1}) 

210 self._write_chart_type({"primary_axes": 0}) 

211 

212 # Write c:catAx and c:valAx elements for series using primary axes. 

213 self._write_cat_val_axis( 

214 { 

215 "x_axis": self.x_axis, 

216 "y_axis": self.y_axis, 

217 "axis_ids": self.axis_ids, 

218 "position": "b", 

219 } 

220 ) 

221 

222 tmp = self.horiz_val_axis 

223 self.horiz_val_axis = 1 

224 

225 self._write_val_axis( 

226 { 

227 "x_axis": self.x_axis, 

228 "y_axis": self.y_axis, 

229 "axis_ids": self.axis_ids, 

230 "position": "l", 

231 } 

232 ) 

233 

234 self.horiz_val_axis = tmp 

235 

236 # Write c:valAx and c:catAx elements for series using secondary axes 

237 self._write_cat_val_axis( 

238 { 

239 "x_axis": self.x2_axis, 

240 "y_axis": self.y2_axis, 

241 "axis_ids": self.axis2_ids, 

242 "position": "b", 

243 } 

244 ) 

245 self.horiz_val_axis = 1 

246 self._write_val_axis( 

247 { 

248 "x_axis": self.x2_axis, 

249 "y_axis": self.y2_axis, 

250 "axis_ids": self.axis2_ids, 

251 "position": "l", 

252 } 

253 ) 

254 

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

256 self._write_sp_pr(self.plotarea) 

257 

258 self._xml_end_tag("c:plotArea") 

259 

260 def _write_x_val(self, series): 

261 # Write the <c:xVal> element. 

262 formula = series.get("categories") 

263 data_id = series.get("cat_data_id") 

264 data = self.formula_data[data_id] 

265 

266 self._xml_start_tag("c:xVal") 

267 

268 # Check the type of cached data. 

269 data_type = self._get_data_type(data) 

270 

271 if data_type == "str": 

272 # Write the c:numRef element. 

273 self._write_str_ref(formula, data, data_type) 

274 else: 

275 # Write the c:numRef element. 

276 self._write_num_ref(formula, data, data_type) 

277 

278 self._xml_end_tag("c:xVal") 

279 

280 def _write_y_val(self, series): 

281 # Write the <c:yVal> element. 

282 formula = series.get("values") 

283 data_id = series.get("val_data_id") 

284 data = self.formula_data[data_id] 

285 

286 self._xml_start_tag("c:yVal") 

287 

288 # Unlike Cat axes data should only be numeric. 

289 # Write the c:numRef element. 

290 self._write_num_ref(formula, data, "num") 

291 

292 self._xml_end_tag("c:yVal") 

293 

294 def _write_scatter_style(self, val): 

295 # Write the <c:scatterStyle> element. 

296 attributes = [("val", val)] 

297 

298 self._xml_empty_tag("c:scatterStyle", attributes) 

299 

300 def _modify_series_formatting(self): 

301 # Add default formatting to the series data unless it has already been 

302 # specified by the user. 

303 subtype = self.subtype 

304 

305 # The default scatter style "markers only" requires a line type. 

306 if subtype == "marker_only": 

307 # Go through each series and define default values. 

308 for series in self.series: 

309 # Set a line type unless there is already a user defined type. 

310 if not series["line"]["defined"]: 

311 series["line"] = { 

312 "width": 2.25, 

313 "none": 1, 

314 "defined": 1, 

315 } 

316 

317 def _write_d_pt_point(self, index, point): 

318 # Write an individual <c:dPt> element. Override the parent method to 

319 # add markers. 

320 

321 self._xml_start_tag("c:dPt") 

322 

323 # Write the c:idx element. 

324 self._write_idx(index) 

325 

326 self._xml_start_tag("c:marker") 

327 

328 # Write the c:spPr element. 

329 self._write_sp_pr(point) 

330 

331 self._xml_end_tag("c:marker") 

332 

333 self._xml_end_tag("c:dPt")