table.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  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. This module provides functionality to add a table to a plot.
  10. Use the factory function `~matplotlib.table.table` to create a ready-made
  11. table from texts. If you need more control, use the `.Table` class and its
  12. methods.
  13. The table consists of a grid of cells, which are indexed by (row, column).
  14. The cell (0, 0) is positioned at the top left.
  15. Thanks to John Gill for providing the class and table.
  16. """
  17. from . import artist, cbook, docstring
  18. from .artist import Artist, allow_rasterization
  19. from .patches import Rectangle
  20. from .text import Text
  21. from .transforms import Bbox
  22. from .path import Path
  23. class Cell(Rectangle):
  24. """
  25. A cell is a `.Rectangle` with some associated `.Text`.
  26. .. note:
  27. As a user, you'll most likely not creates cells yourself. Instead, you
  28. should use either the `~matplotlib.table.table` factory function or
  29. `.Table.add_cell`.
  30. Parameters
  31. ----------
  32. xy : 2-tuple
  33. The position of the bottom left corner of the cell.
  34. width : float
  35. The cell width.
  36. height : float
  37. The cell height.
  38. edgecolor : color
  39. The color of the cell border.
  40. facecolor : color
  41. The cell facecolor.
  42. fill : bool
  43. Whether the cell background is filled.
  44. text : str
  45. The cell text.
  46. loc : {'left', 'center', 'right'}, default: 'right'
  47. The alignment of the text within the cell.
  48. fontproperties : dict
  49. A dict defining the font properties of the text. Supported keys and
  50. values are the keyword arguments accepted by `.FontProperties`.
  51. """
  52. PAD = 0.1
  53. """Padding between text and rectangle."""
  54. def __init__(self, xy, width, height,
  55. edgecolor='k', facecolor='w',
  56. fill=True,
  57. text='',
  58. loc=None,
  59. fontproperties=None
  60. ):
  61. # Call base
  62. Rectangle.__init__(self, xy, width=width, height=height, fill=fill,
  63. edgecolor=edgecolor, facecolor=facecolor)
  64. self.set_clip_on(False)
  65. # Create text object
  66. if loc is None:
  67. loc = 'right'
  68. self._loc = loc
  69. self._text = Text(x=xy[0], y=xy[1], text=text,
  70. fontproperties=fontproperties)
  71. self._text.set_clip_on(False)
  72. def set_transform(self, trans):
  73. Rectangle.set_transform(self, trans)
  74. # the text does not get the transform!
  75. self.stale = True
  76. def set_figure(self, fig):
  77. Rectangle.set_figure(self, fig)
  78. self._text.set_figure(fig)
  79. def get_text(self):
  80. """Return the cell `.Text` instance."""
  81. return self._text
  82. def set_fontsize(self, size):
  83. """Set the text fontsize."""
  84. self._text.set_fontsize(size)
  85. self.stale = True
  86. def get_fontsize(self):
  87. """Return the cell fontsize."""
  88. return self._text.get_fontsize()
  89. def auto_set_font_size(self, renderer):
  90. """Shrink font size until the text fits into the cell width."""
  91. fontsize = self.get_fontsize()
  92. required = self.get_required_width(renderer)
  93. while fontsize > 1 and required > self.get_width():
  94. fontsize -= 1
  95. self.set_fontsize(fontsize)
  96. required = self.get_required_width(renderer)
  97. return fontsize
  98. @allow_rasterization
  99. def draw(self, renderer):
  100. if not self.get_visible():
  101. return
  102. # draw the rectangle
  103. Rectangle.draw(self, renderer)
  104. # position the text
  105. self._set_text_position(renderer)
  106. self._text.draw(renderer)
  107. self.stale = False
  108. def _set_text_position(self, renderer):
  109. """Set text up so it draws in the right place.
  110. Currently support 'left', 'center' and 'right'
  111. """
  112. bbox = self.get_window_extent(renderer)
  113. l, b, w, h = bbox.bounds
  114. # draw in center vertically
  115. self._text.set_verticalalignment('center')
  116. y = b + (h / 2.0)
  117. # now position horizontally
  118. if self._loc == 'center':
  119. self._text.set_horizontalalignment('center')
  120. x = l + (w / 2.0)
  121. elif self._loc == 'left':
  122. self._text.set_horizontalalignment('left')
  123. x = l + (w * self.PAD)
  124. else:
  125. self._text.set_horizontalalignment('right')
  126. x = l + (w * (1.0 - self.PAD))
  127. self._text.set_position((x, y))
  128. def get_text_bounds(self, renderer):
  129. """
  130. Return the text bounds as *(x, y, width, height)* in table coordinates.
  131. """
  132. bbox = self._text.get_window_extent(renderer)
  133. bboxa = bbox.inverse_transformed(self.get_data_transform())
  134. return bboxa.bounds
  135. def get_required_width(self, renderer):
  136. """Return the minimal required width for the cell."""
  137. l, b, w, h = self.get_text_bounds(renderer)
  138. return w * (1.0 + (2.0 * self.PAD))
  139. @docstring.dedent_interpd
  140. def set_text_props(self, **kwargs):
  141. """
  142. Update the text properties.
  143. Valid keyword arguments are:
  144. %(Text)s
  145. """
  146. self._text.update(kwargs)
  147. self.stale = True
  148. class CustomCell(Cell):
  149. """
  150. A `.Cell` subclass with configurable edge visibility.
  151. """
  152. _edges = 'BRTL'
  153. _edge_aliases = {'open': '',
  154. 'closed': _edges, # default
  155. 'horizontal': 'BT',
  156. 'vertical': 'RL'
  157. }
  158. def __init__(self, *args, visible_edges, **kwargs):
  159. super().__init__(*args, **kwargs)
  160. self.visible_edges = visible_edges
  161. @property
  162. def visible_edges(self):
  163. """
  164. The cell edges to be drawn with a line.
  165. Reading this property returns a substring of 'BRTL' (bottom, right,
  166. top, left').
  167. When setting this property, you can use a substring of 'BRTL' or one
  168. of {'open', 'closed', 'horizontal', 'vertical'}.
  169. """
  170. return self._visible_edges
  171. @visible_edges.setter
  172. def visible_edges(self, value):
  173. if value is None:
  174. self._visible_edges = self._edges
  175. elif value in self._edge_aliases:
  176. self._visible_edges = self._edge_aliases[value]
  177. else:
  178. if any(edge not in self._edges for edge in value):
  179. raise ValueError('Invalid edge param {}, must only be one of '
  180. '{} or string of {}'.format(
  181. value,
  182. ", ".join(self._edge_aliases),
  183. ", ".join(self._edges)))
  184. self._visible_edges = value
  185. self.stale = True
  186. def get_path(self):
  187. """Return a `.Path` for the `.visible_edges`."""
  188. codes = [Path.MOVETO]
  189. codes.extend(
  190. Path.LINETO if edge in self._visible_edges else Path.MOVETO
  191. for edge in self._edges)
  192. if Path.MOVETO not in codes[1:]: # All sides are visible
  193. codes[-1] = Path.CLOSEPOLY
  194. return Path(
  195. [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]],
  196. codes,
  197. readonly=True
  198. )
  199. class Table(Artist):
  200. """
  201. A table of cells.
  202. The table consists of a grid of cells, which are indexed by (row, column).
  203. For a simple table, you'll have a full grid of cells with indices from
  204. (0, 0) to (num_rows-1, num_cols-1), in which the cell (0, 0) is positioned
  205. at the top left. However, you can also add cells with negative indices.
  206. You don't have to add a cell to every grid position, so you can create
  207. tables that have holes.
  208. *Note*: You'll usually not create an empty table from scratch. Instead use
  209. `~matplotlib.table.table` to create a table from data.
  210. """
  211. codes = {'best': 0,
  212. 'upper right': 1, # default
  213. 'upper left': 2,
  214. 'lower left': 3,
  215. 'lower right': 4,
  216. 'center left': 5,
  217. 'center right': 6,
  218. 'lower center': 7,
  219. 'upper center': 8,
  220. 'center': 9,
  221. 'top right': 10,
  222. 'top left': 11,
  223. 'bottom left': 12,
  224. 'bottom right': 13,
  225. 'right': 14,
  226. 'left': 15,
  227. 'top': 16,
  228. 'bottom': 17,
  229. }
  230. """Possible values where to place the table relative to the Axes."""
  231. FONTSIZE = 10
  232. AXESPAD = 0.02
  233. """The border between the Axes and the table edge in Axes units."""
  234. def __init__(self, ax, loc=None, bbox=None, **kwargs):
  235. """
  236. Parameters
  237. ----------
  238. ax : `matplotlib.axes.Axes`
  239. The `~.axes.Axes` to plot the table into.
  240. loc : str
  241. The position of the cell with respect to *ax*. This must be one of
  242. the `~.Table.codes`.
  243. bbox : `.Bbox` or None
  244. A bounding box to draw the table into. If this is not *None*, this
  245. overrides *loc*.
  246. Other Parameters
  247. ----------------
  248. **kwargs
  249. `.Artist` properties.
  250. """
  251. Artist.__init__(self)
  252. if isinstance(loc, str):
  253. if loc not in self.codes:
  254. cbook.warn_deprecated(
  255. "3.1", message="Unrecognized location {!r}. Falling back "
  256. "on 'bottom'; valid locations are\n\t{}\n"
  257. "This will raise an exception %(removal)s."
  258. .format(loc, '\n\t'.join(self.codes)))
  259. loc = 'bottom'
  260. loc = self.codes[loc]
  261. self.set_figure(ax.figure)
  262. self._axes = ax
  263. self._loc = loc
  264. self._bbox = bbox
  265. # use axes coords
  266. ax._unstale_viewLim()
  267. self.set_transform(ax.transAxes)
  268. self._cells = {}
  269. self._edges = None
  270. self._autoColumns = []
  271. self._autoFontsize = True
  272. self.update(kwargs)
  273. self.set_clip_on(False)
  274. def add_cell(self, row, col, *args, **kwargs):
  275. """
  276. Create a cell and add it to the table.
  277. Parameters
  278. ----------
  279. row : int
  280. Row index.
  281. col : int
  282. Column index.
  283. *args, **kwargs
  284. All other parameters are passed on to `Cell`.
  285. Returns
  286. -------
  287. cell : `.CustomCell`
  288. The created cell.
  289. """
  290. xy = (0, 0)
  291. cell = CustomCell(xy, visible_edges=self.edges, *args, **kwargs)
  292. self[row, col] = cell
  293. return cell
  294. def __setitem__(self, position, cell):
  295. """
  296. Set a custom cell in a given position.
  297. """
  298. cbook._check_isinstance(CustomCell, cell=cell)
  299. try:
  300. row, col = position[0], position[1]
  301. except Exception:
  302. raise KeyError('Only tuples length 2 are accepted as coordinates')
  303. cell.set_figure(self.figure)
  304. cell.set_transform(self.get_transform())
  305. cell.set_clip_on(False)
  306. self._cells[row, col] = cell
  307. self.stale = True
  308. def __getitem__(self, position):
  309. """Retrieve a custom cell from a given position."""
  310. return self._cells[position]
  311. @property
  312. def edges(self):
  313. """
  314. The default value of `~.CustomCell.visible_edges` for newly added
  315. cells using `.add_cell`.
  316. Notes
  317. -----
  318. This setting does currently only affect newly created cells using
  319. `.add_cell`.
  320. To change existing cells, you have to set their edges explicitly::
  321. for c in tab.get_celld().values():
  322. c.visible_edges = 'horizontal'
  323. """
  324. return self._edges
  325. @edges.setter
  326. def edges(self, value):
  327. self._edges = value
  328. self.stale = True
  329. def _approx_text_height(self):
  330. return (self.FONTSIZE / 72.0 * self.figure.dpi /
  331. self._axes.bbox.height * 1.2)
  332. @allow_rasterization
  333. def draw(self, renderer):
  334. # docstring inherited
  335. # Need a renderer to do hit tests on mouseevent; assume the last one
  336. # will do
  337. if renderer is None:
  338. renderer = self.figure._cachedRenderer
  339. if renderer is None:
  340. raise RuntimeError('No renderer defined')
  341. if not self.get_visible():
  342. return
  343. renderer.open_group('table', gid=self.get_gid())
  344. self._update_positions(renderer)
  345. for key in sorted(self._cells):
  346. self._cells[key].draw(renderer)
  347. renderer.close_group('table')
  348. self.stale = False
  349. def _get_grid_bbox(self, renderer):
  350. """
  351. Get a bbox, in axes co-ordinates for the cells.
  352. Only include those in the range (0, 0) to (maxRow, maxCol).
  353. """
  354. boxes = [cell.get_window_extent(renderer)
  355. for (row, col), cell in self._cells.items()
  356. if row >= 0 and col >= 0]
  357. bbox = Bbox.union(boxes)
  358. return bbox.inverse_transformed(self.get_transform())
  359. def contains(self, mouseevent):
  360. # docstring inherited
  361. inside, info = self._default_contains(mouseevent)
  362. if inside is not None:
  363. return inside, info
  364. # TODO: Return index of the cell containing the cursor so that the user
  365. # doesn't have to bind to each one individually.
  366. renderer = self.figure._cachedRenderer
  367. if renderer is not None:
  368. boxes = [cell.get_window_extent(renderer)
  369. for (row, col), cell in self._cells.items()
  370. if row >= 0 and col >= 0]
  371. bbox = Bbox.union(boxes)
  372. return bbox.contains(mouseevent.x, mouseevent.y), {}
  373. else:
  374. return False, {}
  375. def get_children(self):
  376. """Return the Artists contained by the table."""
  377. return list(self._cells.values())
  378. def get_window_extent(self, renderer):
  379. """Return the bounding box of the table in window coords."""
  380. self._update_positions(renderer)
  381. boxes = [cell.get_window_extent(renderer)
  382. for cell in self._cells.values()]
  383. return Bbox.union(boxes)
  384. def _do_cell_alignment(self):
  385. """
  386. Calculate row heights and column widths; position cells accordingly.
  387. """
  388. # Calculate row/column widths
  389. widths = {}
  390. heights = {}
  391. for (row, col), cell in self._cells.items():
  392. height = heights.setdefault(row, 0.0)
  393. heights[row] = max(height, cell.get_height())
  394. width = widths.setdefault(col, 0.0)
  395. widths[col] = max(width, cell.get_width())
  396. # work out left position for each column
  397. xpos = 0
  398. lefts = {}
  399. for col in sorted(widths):
  400. lefts[col] = xpos
  401. xpos += widths[col]
  402. ypos = 0
  403. bottoms = {}
  404. for row in sorted(heights, reverse=True):
  405. bottoms[row] = ypos
  406. ypos += heights[row]
  407. # set cell positions
  408. for (row, col), cell in self._cells.items():
  409. cell.set_x(lefts[col])
  410. cell.set_y(bottoms[row])
  411. def auto_set_column_width(self, col):
  412. """
  413. Automatically set the widths of given columns to optimal sizes.
  414. Parameters
  415. ----------
  416. col : int or sequence of ints
  417. The indices of the columns to auto-scale.
  418. """
  419. # check for col possibility on iteration
  420. try:
  421. iter(col)
  422. except (TypeError, AttributeError):
  423. self._autoColumns.append(col)
  424. else:
  425. for cell in col:
  426. self._autoColumns.append(cell)
  427. self.stale = True
  428. def _auto_set_column_width(self, col, renderer):
  429. """Automatically set width for column."""
  430. cells = [cell for key, cell in self._cells.items() if key[1] == col]
  431. max_width = max((cell.get_required_width(renderer) for cell in cells),
  432. default=0)
  433. for cell in cells:
  434. cell.set_width(max_width)
  435. def auto_set_font_size(self, value=True):
  436. """Automatically set font size."""
  437. self._autoFontsize = value
  438. self.stale = True
  439. def _auto_set_font_size(self, renderer):
  440. if len(self._cells) == 0:
  441. return
  442. fontsize = next(iter(self._cells.values())).get_fontsize()
  443. cells = []
  444. for key, cell in self._cells.items():
  445. # ignore auto-sized columns
  446. if key[1] in self._autoColumns:
  447. continue
  448. size = cell.auto_set_font_size(renderer)
  449. fontsize = min(fontsize, size)
  450. cells.append(cell)
  451. # now set all fontsizes equal
  452. for cell in self._cells.values():
  453. cell.set_fontsize(fontsize)
  454. def scale(self, xscale, yscale):
  455. """Scale column widths by *xscale* and row heights by *yscale*."""
  456. for c in self._cells.values():
  457. c.set_width(c.get_width() * xscale)
  458. c.set_height(c.get_height() * yscale)
  459. def set_fontsize(self, size):
  460. """
  461. Set the font size, in points, of the cell text.
  462. Parameters
  463. ----------
  464. size : float
  465. Notes
  466. -----
  467. As long as auto font size has not been disabled, the value will be
  468. clipped such that the text fits horizontally into the cell.
  469. You can disable this behavior using `.auto_set_font_size`.
  470. >>> the_table.auto_set_font_size(False)
  471. >>> the_table.set_fontsize(20)
  472. However, there is no automatic scaling of the row height so that the
  473. text may exceed the cell boundary.
  474. """
  475. for cell in self._cells.values():
  476. cell.set_fontsize(size)
  477. self.stale = True
  478. def _offset(self, ox, oy):
  479. """Move all the artists by ox, oy (axes coords)."""
  480. for c in self._cells.values():
  481. x, y = c.get_x(), c.get_y()
  482. c.set_x(x + ox)
  483. c.set_y(y + oy)
  484. def _update_positions(self, renderer):
  485. # called from renderer to allow more precise estimates of
  486. # widths and heights with get_window_extent
  487. # Do any auto width setting
  488. for col in self._autoColumns:
  489. self._auto_set_column_width(col, renderer)
  490. if self._autoFontsize:
  491. self._auto_set_font_size(renderer)
  492. # Align all the cells
  493. self._do_cell_alignment()
  494. bbox = self._get_grid_bbox(renderer)
  495. l, b, w, h = bbox.bounds
  496. if self._bbox is not None:
  497. # Position according to bbox
  498. rl, rb, rw, rh = self._bbox
  499. self.scale(rw / w, rh / h)
  500. ox = rl - l
  501. oy = rb - b
  502. self._do_cell_alignment()
  503. else:
  504. # Position using loc
  505. (BEST, UR, UL, LL, LR, CL, CR, LC, UC, C,
  506. TR, TL, BL, BR, R, L, T, B) = range(len(self.codes))
  507. # defaults for center
  508. ox = (0.5 - w / 2) - l
  509. oy = (0.5 - h / 2) - b
  510. if self._loc in (UL, LL, CL): # left
  511. ox = self.AXESPAD - l
  512. if self._loc in (BEST, UR, LR, R, CR): # right
  513. ox = 1 - (l + w + self.AXESPAD)
  514. if self._loc in (BEST, UR, UL, UC): # upper
  515. oy = 1 - (b + h + self.AXESPAD)
  516. if self._loc in (LL, LR, LC): # lower
  517. oy = self.AXESPAD - b
  518. if self._loc in (LC, UC, C): # center x
  519. ox = (0.5 - w / 2) - l
  520. if self._loc in (CL, CR, C): # center y
  521. oy = (0.5 - h / 2) - b
  522. if self._loc in (TL, BL, L): # out left
  523. ox = - (l + w)
  524. if self._loc in (TR, BR, R): # out right
  525. ox = 1.0 - l
  526. if self._loc in (TR, TL, T): # out top
  527. oy = 1.0 - b
  528. if self._loc in (BL, BR, B): # out bottom
  529. oy = - (b + h)
  530. self._offset(ox, oy)
  531. def get_celld(self):
  532. r"""
  533. Return a dict of cells in the table mapping *(row, column)* to
  534. `.Cell`\s.
  535. Notes
  536. -----
  537. You can also directly index into the Table object to access individual
  538. cells::
  539. cell = table[row, col]
  540. """
  541. return self._cells
  542. docstring.interpd.update(Table=artist.kwdoc(Table))
  543. @docstring.dedent_interpd
  544. def table(ax,
  545. cellText=None, cellColours=None,
  546. cellLoc='right', colWidths=None,
  547. rowLabels=None, rowColours=None, rowLoc='left',
  548. colLabels=None, colColours=None, colLoc='center',
  549. loc='bottom', bbox=None, edges='closed',
  550. **kwargs):
  551. """
  552. Add a table to an `~.axes.Axes`.
  553. At least one of *cellText* or *cellColours* must be specified. These
  554. parameters must be 2D lists, in which the outer lists define the rows and
  555. the inner list define the column values per row. Each row must have the
  556. same number of elements.
  557. The table can optionally have row and column headers, which are configured
  558. using *rowLabels*, *rowColours*, *rowLoc* and *colLabels*, *colColours*,
  559. *colLoc* respectively.
  560. For finer grained control over tables, use the `.Table` class and add it to
  561. the axes with `.Axes.add_table`.
  562. Parameters
  563. ----------
  564. cellText : 2D list of str, optional
  565. The texts to place into the table cells.
  566. *Note*: Line breaks in the strings are currently not accounted for and
  567. will result in the text exceeding the cell boundaries.
  568. cellColours : 2D list of colors, optional
  569. The background colors of the cells.
  570. cellLoc : {'left', 'center', 'right'}, default: 'right'
  571. The alignment of the text within the cells.
  572. colWidths : list of float, optional
  573. The column widths in units of the axes. If not given, all columns will
  574. have a width of *1 / ncols*.
  575. rowLabels : list of str, optional
  576. The text of the row header cells.
  577. rowColours : list of colors, optional
  578. The colors of the row header cells.
  579. rowLoc : {'left', 'center', 'right'}, optional, default: 'left'
  580. The text alignment of the row header cells.
  581. colLabels : list of str, optional
  582. The text of the column header cells.
  583. colColours : list of colors, optional
  584. The colors of the column header cells.
  585. colLoc : {'left', 'center', 'right'}, optional, default: 'left'
  586. The text alignment of the column header cells.
  587. loc : str, optional
  588. The position of the cell with respect to *ax*. This must be one of
  589. the `~.Table.codes`.
  590. bbox : `.Bbox`, optional
  591. A bounding box to draw the table into. If this is not *None*, this
  592. overrides *loc*.
  593. edges : substring of 'BRTL' or {'open', 'closed', 'horizontal', 'vertical'}
  594. The cell edges to be drawn with a line. See also
  595. `~.CustomCell.visible_edges`.
  596. Other Parameters
  597. ----------------
  598. **kwargs
  599. `.Table` properties.
  600. %(Table)s
  601. Returns
  602. -------
  603. table : `~matplotlib.table.Table`
  604. The created table.
  605. """
  606. if cellColours is None and cellText is None:
  607. raise ValueError('At least one argument from "cellColours" or '
  608. '"cellText" must be provided to create a table.')
  609. # Check we have some cellText
  610. if cellText is None:
  611. # assume just colours are needed
  612. rows = len(cellColours)
  613. cols = len(cellColours[0])
  614. cellText = [[''] * cols] * rows
  615. rows = len(cellText)
  616. cols = len(cellText[0])
  617. for row in cellText:
  618. if len(row) != cols:
  619. raise ValueError("Each row in 'cellText' must have {} columns"
  620. .format(cols))
  621. if cellColours is not None:
  622. if len(cellColours) != rows:
  623. raise ValueError("'cellColours' must have {} rows".format(rows))
  624. for row in cellColours:
  625. if len(row) != cols:
  626. raise ValueError("Each row in 'cellColours' must have {} "
  627. "columns".format(cols))
  628. else:
  629. cellColours = ['w' * cols] * rows
  630. # Set colwidths if not given
  631. if colWidths is None:
  632. colWidths = [1.0 / cols] * cols
  633. # Fill in missing information for column
  634. # and row labels
  635. rowLabelWidth = 0
  636. if rowLabels is None:
  637. if rowColours is not None:
  638. rowLabels = [''] * rows
  639. rowLabelWidth = colWidths[0]
  640. elif rowColours is None:
  641. rowColours = 'w' * rows
  642. if rowLabels is not None:
  643. if len(rowLabels) != rows:
  644. raise ValueError("'rowLabels' must be of length {0}".format(rows))
  645. # If we have column labels, need to shift
  646. # the text and colour arrays down 1 row
  647. offset = 1
  648. if colLabels is None:
  649. if colColours is not None:
  650. colLabels = [''] * cols
  651. else:
  652. offset = 0
  653. elif colColours is None:
  654. colColours = 'w' * cols
  655. # Set up cell colours if not given
  656. if cellColours is None:
  657. cellColours = ['w' * cols] * rows
  658. # Now create the table
  659. table = Table(ax, loc, bbox, **kwargs)
  660. table.edges = edges
  661. height = table._approx_text_height()
  662. # Add the cells
  663. for row in range(rows):
  664. for col in range(cols):
  665. table.add_cell(row + offset, col,
  666. width=colWidths[col], height=height,
  667. text=cellText[row][col],
  668. facecolor=cellColours[row][col],
  669. loc=cellLoc)
  670. # Do column labels
  671. if colLabels is not None:
  672. for col in range(cols):
  673. table.add_cell(0, col,
  674. width=colWidths[col], height=height,
  675. text=colLabels[col], facecolor=colColours[col],
  676. loc=colLoc)
  677. # Do row labels
  678. if rowLabels is not None:
  679. for row in range(rows):
  680. table.add_cell(row + offset, -1,
  681. width=rowLabelWidth or 1e-15, height=height,
  682. text=rowLabels[row], facecolor=rowColours[row],
  683. loc=rowLoc)
  684. if rowLabelWidth == 0:
  685. table.auto_set_column_width(-1)
  686. ax.add_table(table)
  687. return table