Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/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

118 statements  

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

2# 

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

4# 

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

6# 

7# Copyright (c) 2013-2025, John McNamara, jmcnamara@cpan.org 

8# 

9 

10from warnings import warn 

11 

12from . import chart 

13 

14 

15class ChartScatter(chart.Chart): 

16 """ 

17 A class for writing the Excel XLSX Scatter charts. 

18 

19 

20 """ 

21 

22 ########################################################################### 

23 # 

24 # Public API. 

25 # 

26 ########################################################################### 

27 

28 def __init__(self, options=None): 

29 """ 

30 Constructor. 

31 

32 """ 

33 super().__init__() 

34 

35 if options is None: 

36 options = {} 

37 

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

39 

40 if not self.subtype: 

41 self.subtype = "marker_only" 

42 

43 self.cross_between = "midCat" 

44 self.horiz_val_axis = 0 

45 self.val_axis_position = "b" 

46 self.smooth_allowed = True 

47 self.requires_category = True 

48 

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

50 self.label_position_default = "right" 

51 self.label_positions = { 

52 "center": "ctr", 

53 "right": "r", 

54 "left": "l", 

55 "above": "t", 

56 "below": "b", 

57 # For backward compatibility. 

58 "top": "t", 

59 "bottom": "b", 

60 } 

61 

62 def combine(self, chart=None): 

63 # pylint: disable=redefined-outer-name 

64 """ 

65 Create a combination chart with a secondary chart. 

66 

67 Note: Override parent method to add a warning. 

68 

69 Args: 

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

71 

72 Returns: 

73 Nothing. 

74 

75 """ 

76 if chart is None: 

77 return 

78 

79 warn( 

80 "Combined chart not currently supported with scatter chart " 

81 "as the primary chart" 

82 ) 

83 

84 ########################################################################### 

85 # 

86 # Private API. 

87 # 

88 ########################################################################### 

89 

90 def _write_chart_type(self, args): 

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

92 # Write the c:scatterChart element. 

93 self._write_scatter_chart(args) 

94 

95 ########################################################################### 

96 # 

97 # XML methods. 

98 # 

99 ########################################################################### 

100 

101 def _write_scatter_chart(self, args): 

102 # Write the <c:scatterChart> element. 

103 

104 if args["primary_axes"]: 

105 series = self._get_primary_axes_series() 

106 else: 

107 series = self._get_secondary_axes_series() 

108 

109 if not series: 

110 return 

111 

112 style = "lineMarker" 

113 subtype = self.subtype 

114 

115 # Set the user defined chart subtype. 

116 if subtype == "marker_only": 

117 style = "lineMarker" 

118 

119 if subtype == "straight_with_markers": 

120 style = "lineMarker" 

121 

122 if subtype == "straight": 

123 style = "lineMarker" 

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

125 

126 if subtype == "smooth_with_markers": 

127 style = "smoothMarker" 

128 

129 if subtype == "smooth": 

130 style = "smoothMarker" 

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

132 

133 # Add default formatting to the series data. 

134 self._modify_series_formatting() 

135 

136 self._xml_start_tag("c:scatterChart") 

137 

138 # Write the c:scatterStyle element. 

139 self._write_scatter_style(style) 

140 

141 # Write the series elements. 

142 for data in series: 

143 self._write_ser(data) 

144 

145 # Write the c:axId elements 

146 self._write_axis_ids(args) 

147 

148 self._xml_end_tag("c:scatterChart") 

149 

150 def _write_ser(self, series): 

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

152 # Write the <c:ser> element. 

153 

154 index = self.series_index 

155 self.series_index += 1 

156 

157 self._xml_start_tag("c:ser") 

158 

159 # Write the c:idx element. 

160 self._write_idx(index) 

161 

162 # Write the c:order element. 

163 self._write_order(index) 

164 

165 # Write the series name. 

166 self._write_series_name(series) 

167 

168 # Write the c:spPr element. 

169 self._write_sp_pr(series) 

170 

171 # Write the c:marker element. 

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

173 

174 # Write the c:dPt element. 

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

176 

177 # Write the c:dLbls element. 

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

179 

180 # Write the c:trendline element. 

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

182 

183 # Write the c:errBars element. 

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

185 

186 # Write the c:xVal element. 

187 self._write_x_val(series) 

188 

189 # Write the c:yVal element. 

190 self._write_y_val(series) 

191 

192 # Write the c:smooth element. 

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

194 # Default is on for smooth scatter charts. 

195 self._write_c_smooth(True) 

196 else: 

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

198 

199 self._xml_end_tag("c:ser") 

200 

201 def _write_plot_area(self): 

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

203 # of catAx/valAx. 

204 # 

205 # Write the <c:plotArea> element. 

206 self._xml_start_tag("c:plotArea") 

207 

208 # Write the c:layout element. 

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

210 

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

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

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

214 

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

216 self._write_cat_val_axis( 

217 { 

218 "x_axis": self.x_axis, 

219 "y_axis": self.y_axis, 

220 "axis_ids": self.axis_ids, 

221 "position": "b", 

222 } 

223 ) 

224 

225 tmp = self.horiz_val_axis 

226 self.horiz_val_axis = 1 

227 

228 self._write_val_axis( 

229 { 

230 "x_axis": self.x_axis, 

231 "y_axis": self.y_axis, 

232 "axis_ids": self.axis_ids, 

233 "position": "l", 

234 } 

235 ) 

236 

237 self.horiz_val_axis = tmp 

238 

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

240 self._write_cat_val_axis( 

241 { 

242 "x_axis": self.x2_axis, 

243 "y_axis": self.y2_axis, 

244 "axis_ids": self.axis2_ids, 

245 "position": "b", 

246 } 

247 ) 

248 self.horiz_val_axis = 1 

249 self._write_val_axis( 

250 { 

251 "x_axis": self.x2_axis, 

252 "y_axis": self.y2_axis, 

253 "axis_ids": self.axis2_ids, 

254 "position": "l", 

255 } 

256 ) 

257 

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

259 self._write_sp_pr(self.plotarea) 

260 

261 self._xml_end_tag("c:plotArea") 

262 

263 def _write_x_val(self, series): 

264 # Write the <c:xVal> element. 

265 formula = series.get("categories") 

266 data_id = series.get("cat_data_id") 

267 data = self.formula_data[data_id] 

268 

269 self._xml_start_tag("c:xVal") 

270 

271 # Check the type of cached data. 

272 data_type = self._get_data_type(data) 

273 

274 if data_type == "str": 

275 # Write the c:numRef element. 

276 self._write_str_ref(formula, data, data_type) 

277 else: 

278 # Write the c:numRef element. 

279 self._write_num_ref(formula, data, data_type) 

280 

281 self._xml_end_tag("c:xVal") 

282 

283 def _write_y_val(self, series): 

284 # Write the <c:yVal> element. 

285 formula = series.get("values") 

286 data_id = series.get("val_data_id") 

287 data = self.formula_data[data_id] 

288 

289 self._xml_start_tag("c:yVal") 

290 

291 # Unlike Cat axes data should only be numeric. 

292 # Write the c:numRef element. 

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

294 

295 self._xml_end_tag("c:yVal") 

296 

297 def _write_scatter_style(self, val): 

298 # Write the <c:scatterStyle> element. 

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

300 

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

302 

303 def _modify_series_formatting(self): 

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

305 # specified by the user. 

306 subtype = self.subtype 

307 

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

309 if subtype == "marker_only": 

310 # Go through each series and define default values. 

311 for series in self.series: 

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

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

314 series["line"] = { 

315 "width": 2.25, 

316 "none": 1, 

317 "defined": 1, 

318 } 

319 

320 def _write_d_pt_point(self, index, point): 

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

322 # add markers. 

323 

324 self._xml_start_tag("c:dPt") 

325 

326 # Write the c:idx element. 

327 self._write_idx(index) 

328 

329 self._xml_start_tag("c:marker") 

330 

331 # Write the c:spPr element. 

332 self._write_sp_pr(point) 

333 

334 self._xml_end_tag("c:marker") 

335 

336 self._xml_end_tag("c:dPt")