Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/matplotlib/table.py: 19%

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

336 statements  

1# Original code by: 

2# John Gill <jng@europe.renre.com> 

3# Copyright 2004 John Gill and John Hunter 

4# 

5# Subsequent changes: 

6# The Matplotlib development team 

7# Copyright The Matplotlib development team 

8 

9""" 

10Tables drawing. 

11 

12.. note:: 

13 The table implementation in Matplotlib is lightly maintained. For a more 

14 featureful table implementation, you may wish to try `blume 

15 <https://github.com/swfiua/blume>`_. 

16 

17Use the factory function `~matplotlib.table.table` to create a ready-made 

18table from texts. If you need more control, use the `.Table` class and its 

19methods. 

20 

21The table consists of a grid of cells, which are indexed by (row, column). 

22The cell (0, 0) is positioned at the top left. 

23 

24Thanks to John Gill for providing the class and table. 

25""" 

26 

27import numpy as np 

28 

29from . import _api, _docstring 

30from .artist import Artist, allow_rasterization 

31from .patches import Rectangle 

32from .text import Text 

33from .transforms import Bbox 

34from .path import Path 

35 

36 

37class Cell(Rectangle): 

38 """ 

39 A cell is a `.Rectangle` with some associated `.Text`. 

40 

41 As a user, you'll most likely not creates cells yourself. Instead, you 

42 should use either the `~matplotlib.table.table` factory function or 

43 `.Table.add_cell`. 

44 """ 

45 

46 PAD = 0.1 

47 """Padding between text and rectangle.""" 

48 

49 _edges = 'BRTL' 

50 _edge_aliases = {'open': '', 

51 'closed': _edges, # default 

52 'horizontal': 'BT', 

53 'vertical': 'RL' 

54 } 

55 

56 def __init__(self, xy, width, height, *, 

57 edgecolor='k', facecolor='w', 

58 fill=True, 

59 text='', 

60 loc='right', 

61 fontproperties=None, 

62 visible_edges='closed', 

63 ): 

64 """ 

65 Parameters 

66 ---------- 

67 xy : 2-tuple 

68 The position of the bottom left corner of the cell. 

69 width : float 

70 The cell width. 

71 height : float 

72 The cell height. 

73 edgecolor : :mpltype:`color`, default: 'k' 

74 The color of the cell border. 

75 facecolor : :mpltype:`color`, default: 'w' 

76 The cell facecolor. 

77 fill : bool, default: True 

78 Whether the cell background is filled. 

79 text : str, optional 

80 The cell text. 

81 loc : {'right', 'center', 'left'} 

82 The alignment of the text within the cell. 

83 fontproperties : dict, optional 

84 A dict defining the font properties of the text. Supported keys and 

85 values are the keyword arguments accepted by `.FontProperties`. 

86 visible_edges : {'closed', 'open', 'horizontal', 'vertical'} or \ 

87substring of 'BRTL' 

88 The cell edges to be drawn with a line: a substring of 'BRTL' 

89 (bottom, right, top, left), or one of 'open' (no edges drawn), 

90 'closed' (all edges drawn), 'horizontal' (bottom and top), 

91 'vertical' (right and left). 

92 """ 

93 

94 # Call base 

95 super().__init__(xy, width=width, height=height, fill=fill, 

96 edgecolor=edgecolor, facecolor=facecolor) 

97 self.set_clip_on(False) 

98 self.visible_edges = visible_edges 

99 

100 # Create text object 

101 self._loc = loc 

102 self._text = Text(x=xy[0], y=xy[1], clip_on=False, 

103 text=text, fontproperties=fontproperties, 

104 horizontalalignment=loc, verticalalignment='center') 

105 

106 @_api.rename_parameter("3.8", "trans", "t") 

107 def set_transform(self, t): 

108 super().set_transform(t) 

109 # the text does not get the transform! 

110 self.stale = True 

111 

112 def set_figure(self, fig): 

113 super().set_figure(fig) 

114 self._text.set_figure(fig) 

115 

116 def get_text(self): 

117 """Return the cell `.Text` instance.""" 

118 return self._text 

119 

120 def set_fontsize(self, size): 

121 """Set the text fontsize.""" 

122 self._text.set_fontsize(size) 

123 self.stale = True 

124 

125 def get_fontsize(self): 

126 """Return the cell fontsize.""" 

127 return self._text.get_fontsize() 

128 

129 def auto_set_font_size(self, renderer): 

130 """Shrink font size until the text fits into the cell width.""" 

131 fontsize = self.get_fontsize() 

132 required = self.get_required_width(renderer) 

133 while fontsize > 1 and required > self.get_width(): 

134 fontsize -= 1 

135 self.set_fontsize(fontsize) 

136 required = self.get_required_width(renderer) 

137 

138 return fontsize 

139 

140 @allow_rasterization 

141 def draw(self, renderer): 

142 if not self.get_visible(): 

143 return 

144 # draw the rectangle 

145 super().draw(renderer) 

146 # position the text 

147 self._set_text_position(renderer) 

148 self._text.draw(renderer) 

149 self.stale = False 

150 

151 def _set_text_position(self, renderer): 

152 """Set text up so it is drawn in the right place.""" 

153 bbox = self.get_window_extent(renderer) 

154 # center vertically 

155 y = bbox.y0 + bbox.height / 2 

156 # position horizontally 

157 loc = self._text.get_horizontalalignment() 

158 if loc == 'center': 

159 x = bbox.x0 + bbox.width / 2 

160 elif loc == 'left': 

161 x = bbox.x0 + bbox.width * self.PAD 

162 else: # right. 

163 x = bbox.x0 + bbox.width * (1 - self.PAD) 

164 self._text.set_position((x, y)) 

165 

166 def get_text_bounds(self, renderer): 

167 """ 

168 Return the text bounds as *(x, y, width, height)* in table coordinates. 

169 """ 

170 return (self._text.get_window_extent(renderer) 

171 .transformed(self.get_data_transform().inverted()) 

172 .bounds) 

173 

174 def get_required_width(self, renderer): 

175 """Return the minimal required width for the cell.""" 

176 l, b, w, h = self.get_text_bounds(renderer) 

177 return w * (1.0 + (2.0 * self.PAD)) 

178 

179 @_docstring.dedent_interpd 

180 def set_text_props(self, **kwargs): 

181 """ 

182 Update the text properties. 

183 

184 Valid keyword arguments are: 

185 

186 %(Text:kwdoc)s 

187 """ 

188 self._text._internal_update(kwargs) 

189 self.stale = True 

190 

191 @property 

192 def visible_edges(self): 

193 """ 

194 The cell edges to be drawn with a line. 

195 

196 Reading this property returns a substring of 'BRTL' (bottom, right, 

197 top, left'). 

198 

199 When setting this property, you can use a substring of 'BRTL' or one 

200 of {'open', 'closed', 'horizontal', 'vertical'}. 

201 """ 

202 return self._visible_edges 

203 

204 @visible_edges.setter 

205 def visible_edges(self, value): 

206 if value is None: 

207 self._visible_edges = self._edges 

208 elif value in self._edge_aliases: 

209 self._visible_edges = self._edge_aliases[value] 

210 else: 

211 if any(edge not in self._edges for edge in value): 

212 raise ValueError('Invalid edge param {}, must only be one of ' 

213 '{} or string of {}'.format( 

214 value, 

215 ", ".join(self._edge_aliases), 

216 ", ".join(self._edges))) 

217 self._visible_edges = value 

218 self.stale = True 

219 

220 def get_path(self): 

221 """Return a `.Path` for the `.visible_edges`.""" 

222 codes = [Path.MOVETO] 

223 codes.extend( 

224 Path.LINETO if edge in self._visible_edges else Path.MOVETO 

225 for edge in self._edges) 

226 if Path.MOVETO not in codes[1:]: # All sides are visible 

227 codes[-1] = Path.CLOSEPOLY 

228 return Path( 

229 [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]], 

230 codes, 

231 readonly=True 

232 ) 

233 

234 

235CustomCell = Cell # Backcompat. alias. 

236 

237 

238class Table(Artist): 

239 """ 

240 A table of cells. 

241 

242 The table consists of a grid of cells, which are indexed by (row, column). 

243 

244 For a simple table, you'll have a full grid of cells with indices from 

245 (0, 0) to (num_rows-1, num_cols-1), in which the cell (0, 0) is positioned 

246 at the top left. However, you can also add cells with negative indices. 

247 You don't have to add a cell to every grid position, so you can create 

248 tables that have holes. 

249 

250 *Note*: You'll usually not create an empty table from scratch. Instead use 

251 `~matplotlib.table.table` to create a table from data. 

252 """ 

253 codes = {'best': 0, 

254 'upper right': 1, # default 

255 'upper left': 2, 

256 'lower left': 3, 

257 'lower right': 4, 

258 'center left': 5, 

259 'center right': 6, 

260 'lower center': 7, 

261 'upper center': 8, 

262 'center': 9, 

263 'top right': 10, 

264 'top left': 11, 

265 'bottom left': 12, 

266 'bottom right': 13, 

267 'right': 14, 

268 'left': 15, 

269 'top': 16, 

270 'bottom': 17, 

271 } 

272 """Possible values where to place the table relative to the Axes.""" 

273 

274 FONTSIZE = 10 

275 

276 AXESPAD = 0.02 

277 """The border between the Axes and the table edge in Axes units.""" 

278 

279 def __init__(self, ax, loc=None, bbox=None, **kwargs): 

280 """ 

281 Parameters 

282 ---------- 

283 ax : `~matplotlib.axes.Axes` 

284 The `~.axes.Axes` to plot the table into. 

285 loc : str, optional 

286 The position of the cell with respect to *ax*. This must be one of 

287 the `~.Table.codes`. 

288 bbox : `.Bbox` or [xmin, ymin, width, height], optional 

289 A bounding box to draw the table into. If this is not *None*, this 

290 overrides *loc*. 

291 

292 Other Parameters 

293 ---------------- 

294 **kwargs 

295 `.Artist` properties. 

296 """ 

297 

298 super().__init__() 

299 

300 if isinstance(loc, str): 

301 if loc not in self.codes: 

302 raise ValueError( 

303 "Unrecognized location {!r}. Valid locations are\n\t{}" 

304 .format(loc, '\n\t'.join(self.codes))) 

305 loc = self.codes[loc] 

306 self.set_figure(ax.figure) 

307 self._axes = ax 

308 self._loc = loc 

309 self._bbox = bbox 

310 

311 # use axes coords 

312 ax._unstale_viewLim() 

313 self.set_transform(ax.transAxes) 

314 

315 self._cells = {} 

316 self._edges = None 

317 self._autoColumns = [] 

318 self._autoFontsize = True 

319 self._internal_update(kwargs) 

320 

321 self.set_clip_on(False) 

322 

323 def add_cell(self, row, col, *args, **kwargs): 

324 """ 

325 Create a cell and add it to the table. 

326 

327 Parameters 

328 ---------- 

329 row : int 

330 Row index. 

331 col : int 

332 Column index. 

333 *args, **kwargs 

334 All other parameters are passed on to `Cell`. 

335 

336 Returns 

337 ------- 

338 `.Cell` 

339 The created cell. 

340 

341 """ 

342 xy = (0, 0) 

343 cell = Cell(xy, visible_edges=self.edges, *args, **kwargs) 

344 self[row, col] = cell 

345 return cell 

346 

347 def __setitem__(self, position, cell): 

348 """ 

349 Set a custom cell in a given position. 

350 """ 

351 _api.check_isinstance(Cell, cell=cell) 

352 try: 

353 row, col = position[0], position[1] 

354 except Exception as err: 

355 raise KeyError('Only tuples length 2 are accepted as ' 

356 'coordinates') from err 

357 cell.set_figure(self.figure) 

358 cell.set_transform(self.get_transform()) 

359 cell.set_clip_on(False) 

360 self._cells[row, col] = cell 

361 self.stale = True 

362 

363 def __getitem__(self, position): 

364 """Retrieve a custom cell from a given position.""" 

365 return self._cells[position] 

366 

367 @property 

368 def edges(self): 

369 """ 

370 The default value of `~.Cell.visible_edges` for newly added 

371 cells using `.add_cell`. 

372 

373 Notes 

374 ----- 

375 This setting does currently only affect newly created cells using 

376 `.add_cell`. 

377 

378 To change existing cells, you have to set their edges explicitly:: 

379 

380 for c in tab.get_celld().values(): 

381 c.visible_edges = 'horizontal' 

382 

383 """ 

384 return self._edges 

385 

386 @edges.setter 

387 def edges(self, value): 

388 self._edges = value 

389 self.stale = True 

390 

391 def _approx_text_height(self): 

392 return (self.FONTSIZE / 72.0 * self.figure.dpi / 

393 self._axes.bbox.height * 1.2) 

394 

395 @allow_rasterization 

396 def draw(self, renderer): 

397 # docstring inherited 

398 

399 # Need a renderer to do hit tests on mouseevent; assume the last one 

400 # will do 

401 if renderer is None: 

402 renderer = self.figure._get_renderer() 

403 if renderer is None: 

404 raise RuntimeError('No renderer defined') 

405 

406 if not self.get_visible(): 

407 return 

408 renderer.open_group('table', gid=self.get_gid()) 

409 self._update_positions(renderer) 

410 

411 for key in sorted(self._cells): 

412 self._cells[key].draw(renderer) 

413 

414 renderer.close_group('table') 

415 self.stale = False 

416 

417 def _get_grid_bbox(self, renderer): 

418 """ 

419 Get a bbox, in axes coordinates for the cells. 

420 

421 Only include those in the range (0, 0) to (maxRow, maxCol). 

422 """ 

423 boxes = [cell.get_window_extent(renderer) 

424 for (row, col), cell in self._cells.items() 

425 if row >= 0 and col >= 0] 

426 bbox = Bbox.union(boxes) 

427 return bbox.transformed(self.get_transform().inverted()) 

428 

429 def contains(self, mouseevent): 

430 # docstring inherited 

431 if self._different_canvas(mouseevent): 

432 return False, {} 

433 # TODO: Return index of the cell containing the cursor so that the user 

434 # doesn't have to bind to each one individually. 

435 renderer = self.figure._get_renderer() 

436 if renderer is not None: 

437 boxes = [cell.get_window_extent(renderer) 

438 for (row, col), cell in self._cells.items() 

439 if row >= 0 and col >= 0] 

440 bbox = Bbox.union(boxes) 

441 return bbox.contains(mouseevent.x, mouseevent.y), {} 

442 else: 

443 return False, {} 

444 

445 def get_children(self): 

446 """Return the Artists contained by the table.""" 

447 return list(self._cells.values()) 

448 

449 def get_window_extent(self, renderer=None): 

450 # docstring inherited 

451 if renderer is None: 

452 renderer = self.figure._get_renderer() 

453 self._update_positions(renderer) 

454 boxes = [cell.get_window_extent(renderer) 

455 for cell in self._cells.values()] 

456 return Bbox.union(boxes) 

457 

458 def _do_cell_alignment(self): 

459 """ 

460 Calculate row heights and column widths; position cells accordingly. 

461 """ 

462 # Calculate row/column widths 

463 widths = {} 

464 heights = {} 

465 for (row, col), cell in self._cells.items(): 

466 height = heights.setdefault(row, 0.0) 

467 heights[row] = max(height, cell.get_height()) 

468 width = widths.setdefault(col, 0.0) 

469 widths[col] = max(width, cell.get_width()) 

470 

471 # work out left position for each column 

472 xpos = 0 

473 lefts = {} 

474 for col in sorted(widths): 

475 lefts[col] = xpos 

476 xpos += widths[col] 

477 

478 ypos = 0 

479 bottoms = {} 

480 for row in sorted(heights, reverse=True): 

481 bottoms[row] = ypos 

482 ypos += heights[row] 

483 

484 # set cell positions 

485 for (row, col), cell in self._cells.items(): 

486 cell.set_x(lefts[col]) 

487 cell.set_y(bottoms[row]) 

488 

489 def auto_set_column_width(self, col): 

490 """ 

491 Automatically set the widths of given columns to optimal sizes. 

492 

493 Parameters 

494 ---------- 

495 col : int or sequence of ints 

496 The indices of the columns to auto-scale. 

497 """ 

498 col1d = np.atleast_1d(col) 

499 if not np.issubdtype(col1d.dtype, np.integer): 

500 _api.warn_deprecated("3.8", name="col", 

501 message="%(name)r must be an int or sequence of ints. " 

502 "Passing other types is deprecated since %(since)s " 

503 "and will be removed %(removal)s.") 

504 return 

505 for cell in col1d: 

506 self._autoColumns.append(cell) 

507 

508 self.stale = True 

509 

510 def _auto_set_column_width(self, col, renderer): 

511 """Automatically set width for column.""" 

512 cells = [cell for key, cell in self._cells.items() if key[1] == col] 

513 max_width = max((cell.get_required_width(renderer) for cell in cells), 

514 default=0) 

515 for cell in cells: 

516 cell.set_width(max_width) 

517 

518 def auto_set_font_size(self, value=True): 

519 """Automatically set font size.""" 

520 self._autoFontsize = value 

521 self.stale = True 

522 

523 def _auto_set_font_size(self, renderer): 

524 

525 if len(self._cells) == 0: 

526 return 

527 fontsize = next(iter(self._cells.values())).get_fontsize() 

528 cells = [] 

529 for key, cell in self._cells.items(): 

530 # ignore auto-sized columns 

531 if key[1] in self._autoColumns: 

532 continue 

533 size = cell.auto_set_font_size(renderer) 

534 fontsize = min(fontsize, size) 

535 cells.append(cell) 

536 

537 # now set all fontsizes equal 

538 for cell in self._cells.values(): 

539 cell.set_fontsize(fontsize) 

540 

541 def scale(self, xscale, yscale): 

542 """Scale column widths by *xscale* and row heights by *yscale*.""" 

543 for c in self._cells.values(): 

544 c.set_width(c.get_width() * xscale) 

545 c.set_height(c.get_height() * yscale) 

546 

547 def set_fontsize(self, size): 

548 """ 

549 Set the font size, in points, of the cell text. 

550 

551 Parameters 

552 ---------- 

553 size : float 

554 

555 Notes 

556 ----- 

557 As long as auto font size has not been disabled, the value will be 

558 clipped such that the text fits horizontally into the cell. 

559 

560 You can disable this behavior using `.auto_set_font_size`. 

561 

562 >>> the_table.auto_set_font_size(False) 

563 >>> the_table.set_fontsize(20) 

564 

565 However, there is no automatic scaling of the row height so that the 

566 text may exceed the cell boundary. 

567 """ 

568 for cell in self._cells.values(): 

569 cell.set_fontsize(size) 

570 self.stale = True 

571 

572 def _offset(self, ox, oy): 

573 """Move all the artists by ox, oy (axes coords).""" 

574 for c in self._cells.values(): 

575 x, y = c.get_x(), c.get_y() 

576 c.set_x(x + ox) 

577 c.set_y(y + oy) 

578 

579 def _update_positions(self, renderer): 

580 # called from renderer to allow more precise estimates of 

581 # widths and heights with get_window_extent 

582 

583 # Do any auto width setting 

584 for col in self._autoColumns: 

585 self._auto_set_column_width(col, renderer) 

586 

587 if self._autoFontsize: 

588 self._auto_set_font_size(renderer) 

589 

590 # Align all the cells 

591 self._do_cell_alignment() 

592 

593 bbox = self._get_grid_bbox(renderer) 

594 l, b, w, h = bbox.bounds 

595 

596 if self._bbox is not None: 

597 # Position according to bbox 

598 if isinstance(self._bbox, Bbox): 

599 rl, rb, rw, rh = self._bbox.bounds 

600 else: 

601 rl, rb, rw, rh = self._bbox 

602 self.scale(rw / w, rh / h) 

603 ox = rl - l 

604 oy = rb - b 

605 self._do_cell_alignment() 

606 else: 

607 # Position using loc 

608 (BEST, UR, UL, LL, LR, CL, CR, LC, UC, C, 

609 TR, TL, BL, BR, R, L, T, B) = range(len(self.codes)) 

610 # defaults for center 

611 ox = (0.5 - w / 2) - l 

612 oy = (0.5 - h / 2) - b 

613 if self._loc in (UL, LL, CL): # left 

614 ox = self.AXESPAD - l 

615 if self._loc in (BEST, UR, LR, R, CR): # right 

616 ox = 1 - (l + w + self.AXESPAD) 

617 if self._loc in (BEST, UR, UL, UC): # upper 

618 oy = 1 - (b + h + self.AXESPAD) 

619 if self._loc in (LL, LR, LC): # lower 

620 oy = self.AXESPAD - b 

621 if self._loc in (LC, UC, C): # center x 

622 ox = (0.5 - w / 2) - l 

623 if self._loc in (CL, CR, C): # center y 

624 oy = (0.5 - h / 2) - b 

625 

626 if self._loc in (TL, BL, L): # out left 

627 ox = - (l + w) 

628 if self._loc in (TR, BR, R): # out right 

629 ox = 1.0 - l 

630 if self._loc in (TR, TL, T): # out top 

631 oy = 1.0 - b 

632 if self._loc in (BL, BR, B): # out bottom 

633 oy = - (b + h) 

634 

635 self._offset(ox, oy) 

636 

637 def get_celld(self): 

638 r""" 

639 Return a dict of cells in the table mapping *(row, column)* to 

640 `.Cell`\s. 

641 

642 Notes 

643 ----- 

644 You can also directly index into the Table object to access individual 

645 cells:: 

646 

647 cell = table[row, col] 

648 

649 """ 

650 return self._cells 

651 

652 

653@_docstring.dedent_interpd 

654def table(ax, 

655 cellText=None, cellColours=None, 

656 cellLoc='right', colWidths=None, 

657 rowLabels=None, rowColours=None, rowLoc='left', 

658 colLabels=None, colColours=None, colLoc='center', 

659 loc='bottom', bbox=None, edges='closed', 

660 **kwargs): 

661 """ 

662 Add a table to an `~.axes.Axes`. 

663 

664 At least one of *cellText* or *cellColours* must be specified. These 

665 parameters must be 2D lists, in which the outer lists define the rows and 

666 the inner list define the column values per row. Each row must have the 

667 same number of elements. 

668 

669 The table can optionally have row and column headers, which are configured 

670 using *rowLabels*, *rowColours*, *rowLoc* and *colLabels*, *colColours*, 

671 *colLoc* respectively. 

672 

673 For finer grained control over tables, use the `.Table` class and add it to 

674 the Axes with `.Axes.add_table`. 

675 

676 Parameters 

677 ---------- 

678 cellText : 2D list of str, optional 

679 The texts to place into the table cells. 

680 

681 *Note*: Line breaks in the strings are currently not accounted for and 

682 will result in the text exceeding the cell boundaries. 

683 

684 cellColours : 2D list of :mpltype:`color`, optional 

685 The background colors of the cells. 

686 

687 cellLoc : {'right', 'center', 'left'} 

688 The alignment of the text within the cells. 

689 

690 colWidths : list of float, optional 

691 The column widths in units of the axes. If not given, all columns will 

692 have a width of *1 / ncols*. 

693 

694 rowLabels : list of str, optional 

695 The text of the row header cells. 

696 

697 rowColours : list of :mpltype:`color`, optional 

698 The colors of the row header cells. 

699 

700 rowLoc : {'left', 'center', 'right'} 

701 The text alignment of the row header cells. 

702 

703 colLabels : list of str, optional 

704 The text of the column header cells. 

705 

706 colColours : list of :mpltype:`color`, optional 

707 The colors of the column header cells. 

708 

709 colLoc : {'center', 'left', 'right'} 

710 The text alignment of the column header cells. 

711 

712 loc : str, default: 'bottom' 

713 The position of the cell with respect to *ax*. This must be one of 

714 the `~.Table.codes`. 

715 

716 bbox : `.Bbox` or [xmin, ymin, width, height], optional 

717 A bounding box to draw the table into. If this is not *None*, this 

718 overrides *loc*. 

719 

720 edges : {'closed', 'open', 'horizontal', 'vertical'} or substring of 'BRTL' 

721 The cell edges to be drawn with a line. See also 

722 `~.Cell.visible_edges`. 

723 

724 Returns 

725 ------- 

726 `~matplotlib.table.Table` 

727 The created table. 

728 

729 Other Parameters 

730 ---------------- 

731 **kwargs 

732 `.Table` properties. 

733 

734 %(Table:kwdoc)s 

735 """ 

736 

737 if cellColours is None and cellText is None: 

738 raise ValueError('At least one argument from "cellColours" or ' 

739 '"cellText" must be provided to create a table.') 

740 

741 # Check we have some cellText 

742 if cellText is None: 

743 # assume just colours are needed 

744 rows = len(cellColours) 

745 cols = len(cellColours[0]) 

746 cellText = [[''] * cols] * rows 

747 

748 rows = len(cellText) 

749 cols = len(cellText[0]) 

750 for row in cellText: 

751 if len(row) != cols: 

752 raise ValueError(f"Each row in 'cellText' must have {cols} " 

753 "columns") 

754 

755 if cellColours is not None: 

756 if len(cellColours) != rows: 

757 raise ValueError(f"'cellColours' must have {rows} rows") 

758 for row in cellColours: 

759 if len(row) != cols: 

760 raise ValueError("Each row in 'cellColours' must have " 

761 f"{cols} columns") 

762 else: 

763 cellColours = ['w' * cols] * rows 

764 

765 # Set colwidths if not given 

766 if colWidths is None: 

767 colWidths = [1.0 / cols] * cols 

768 

769 # Fill in missing information for column 

770 # and row labels 

771 rowLabelWidth = 0 

772 if rowLabels is None: 

773 if rowColours is not None: 

774 rowLabels = [''] * rows 

775 rowLabelWidth = colWidths[0] 

776 elif rowColours is None: 

777 rowColours = 'w' * rows 

778 

779 if rowLabels is not None: 

780 if len(rowLabels) != rows: 

781 raise ValueError(f"'rowLabels' must be of length {rows}") 

782 

783 # If we have column labels, need to shift 

784 # the text and colour arrays down 1 row 

785 offset = 1 

786 if colLabels is None: 

787 if colColours is not None: 

788 colLabels = [''] * cols 

789 else: 

790 offset = 0 

791 elif colColours is None: 

792 colColours = 'w' * cols 

793 

794 # Set up cell colours if not given 

795 if cellColours is None: 

796 cellColours = ['w' * cols] * rows 

797 

798 # Now create the table 

799 table = Table(ax, loc, bbox, **kwargs) 

800 table.edges = edges 

801 height = table._approx_text_height() 

802 

803 # Add the cells 

804 for row in range(rows): 

805 for col in range(cols): 

806 table.add_cell(row + offset, col, 

807 width=colWidths[col], height=height, 

808 text=cellText[row][col], 

809 facecolor=cellColours[row][col], 

810 loc=cellLoc) 

811 # Do column labels 

812 if colLabels is not None: 

813 for col in range(cols): 

814 table.add_cell(0, col, 

815 width=colWidths[col], height=height, 

816 text=colLabels[col], facecolor=colColours[col], 

817 loc=colLoc) 

818 

819 # Do row labels 

820 if rowLabels is not None: 

821 for row in range(rows): 

822 table.add_cell(row + offset, -1, 

823 width=rowLabelWidth or 1e-15, height=height, 

824 text=rowLabels[row], facecolor=rowColours[row], 

825 loc=rowLoc) 

826 if rowLabelWidth == 0: 

827 table.auto_set_column_width(-1) 

828 

829 ax.add_table(table) 

830 return table