Skip to content

Commit 62cce14

Browse files
committed
Merge pull request matplotlib#4259 from dkua/feature_4044
ENH : Make cell edges in Table configurable Closes matplotlib#4044
2 parents be216b2 + 17576d8 commit 62cce14

File tree

4 files changed

+138
-12
lines changed

4 files changed

+138
-12
lines changed

doc/users/whats_new/updated_table.rst

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Updated Table and to control edge visibility
2+
--------------------------------------------
3+
Added the ability to toggle the visibility of lines in Tables.
4+
Functionality added to the table() factory function under the keyword argument "edges".
5+
Values can be the strings "open", "closed", "horizontal", "vertical" or combinations of the letters "L", "R", "T", "B" which represent left, right, top, and bottom respectively.
6+
7+
Example:
8+
table(..., edges="open") # No line visible
9+
table(..., edges="closed") # All lines visible
10+
table(..., edges="horizontal") # Only top and bottom lines visible
11+
table(..., edges="LT") # Only left and top lines visible.

lib/matplotlib/table.py

+85-12
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from matplotlib import docstring
3535
from .text import Text
3636
from .transforms import Bbox
37+
from matplotlib.path import Path
3738

3839

3940
class Cell(Rectangle):
@@ -146,6 +147,67 @@ def set_text_props(self, **kwargs):
146147
self._text.update(kwargs)
147148

148149

150+
class CustomCell(Cell):
151+
"""
152+
A subclass of Cell where the sides may be visibly toggled.
153+
154+
"""
155+
156+
_edges = 'BRTL'
157+
_edge_aliases = {'open': '',
158+
'closed': _edges, # default
159+
'horizontal': 'BT',
160+
'vertical': 'RL'
161+
}
162+
163+
def __init__(self, *args, **kwargs):
164+
visible_edges = kwargs.pop('visible_edges')
165+
Cell.__init__(self, *args, **kwargs)
166+
self.visible_edges = visible_edges
167+
168+
@property
169+
def visible_edges(self):
170+
return self._visible_edges
171+
172+
@visible_edges.setter
173+
def visible_edges(self, value):
174+
if value is None:
175+
self._visible_edges = self._edges
176+
elif value in self._edge_aliases:
177+
self._visible_edges = self._edge_aliases[value]
178+
else:
179+
for edge in value:
180+
if edge not in self._edges:
181+
msg = ('Invalid edge param {0}, must only be one of'
182+
' {1} or string of {2}.').format(
183+
value,
184+
", ".join(self._edge_aliases.keys()),
185+
", ".join(self._edges),
186+
)
187+
raise ValueError(msg)
188+
self._visible_edges = value
189+
190+
def get_path(self):
191+
'Return a path where the edges specificed by _visible_edges are drawn'
192+
193+
codes = [Path.MOVETO]
194+
195+
for edge in self._edges:
196+
if edge in self._visible_edges:
197+
codes.append(Path.LINETO)
198+
else:
199+
codes.append(Path.MOVETO)
200+
201+
if Path.MOVETO not in codes[1:]: # All sides are visible
202+
codes[-1] = Path.CLOSEPOLY
203+
204+
return Path(
205+
[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]],
206+
codes,
207+
readonly=True
208+
)
209+
210+
149211
class Table(Artist):
150212
"""
151213
Create a table of cells.
@@ -203,6 +265,7 @@ def __init__(self, ax, loc=None, bbox=None, **kwargs):
203265

204266
self._texts = []
205267
self._cells = {}
268+
self._edges = None
206269
self._autoRows = []
207270
self._autoColumns = []
208271
self._autoFontsize = True
@@ -216,13 +279,21 @@ def add_cell(self, row, col, *args, **kwargs):
216279
""" Add a cell to the table. """
217280
xy = (0, 0)
218281

219-
cell = Cell(xy, *args, **kwargs)
282+
cell = CustomCell(xy, visible_edges=self.edges, *args, **kwargs)
220283
cell.set_figure(self.figure)
221284
cell.set_transform(self.get_transform())
222285

223286
cell.set_clip_on(False)
224287
self._cells[(row, col)] = cell
225288

289+
@property
290+
def edges(self):
291+
return self._edges
292+
293+
@edges.setter
294+
def edges(self, value):
295+
self._edges = value
296+
226297
def _approx_text_height(self):
227298
return (self.FONTSIZE / 72.0 * self.figure.dpi /
228299
self._axes.bbox.height * 1.2)
@@ -246,8 +317,8 @@ def draw(self, renderer):
246317
keys.sort()
247318
for key in keys:
248319
self._cells[key].draw(renderer)
249-
#for c in self._cells.itervalues():
250-
# c.draw(renderer)
320+
# for c in self._cells.itervalues():
321+
# c.draw(renderer)
251322
renderer.close_group('table')
252323

253324
def _get_grid_bbox(self, renderer):
@@ -273,8 +344,8 @@ def contains(self, mouseevent):
273344
# doesn't have to bind to each one individually.
274345
if self._cachedRenderer is not None:
275346
boxes = [self._cells[pos].get_window_extent(self._cachedRenderer)
276-
for pos in six.iterkeys(self._cells)
277-
if pos[0] >= 0 and pos[1] >= 0]
347+
for pos in six.iterkeys(self._cells)
348+
if pos[0] >= 0 and pos[1] >= 0]
278349
bbox = Bbox.union(boxes)
279350
return bbox.contains(mouseevent.x, mouseevent.y), {}
280351
else:
@@ -455,23 +526,24 @@ def get_celld(self):
455526

456527

457528
def table(ax,
458-
cellText=None, cellColours=None,
459-
cellLoc='right', colWidths=None,
460-
rowLabels=None, rowColours=None, rowLoc='left',
461-
colLabels=None, colColours=None, colLoc='center',
462-
loc='bottom', bbox=None,
463-
**kwargs):
529+
cellText=None, cellColours=None,
530+
cellLoc='right', colWidths=None,
531+
rowLabels=None, rowColours=None, rowLoc='left',
532+
colLabels=None, colColours=None, colLoc='center',
533+
loc='bottom', bbox=None, edges='closed',
534+
**kwargs):
464535
"""
465536
TABLE(cellText=None, cellColours=None,
466537
cellLoc='right', colWidths=None,
467538
rowLabels=None, rowColours=None, rowLoc='left',
468539
colLabels=None, colColours=None, colLoc='center',
469-
loc='bottom', bbox=None)
540+
loc='bottom', bbox=None, edges='closed')
470541
471542
Factory function to generate a Table instance.
472543
473544
Thanks to John Gill for providing the class and table.
474545
"""
546+
475547
# Check we have some cellText
476548
if cellText is None:
477549
# assume just colours are needed
@@ -531,6 +603,7 @@ def table(ax,
531603

532604
# Now create the table
533605
table = Table(ax, loc, bbox, **kwargs)
606+
table.edges = edges
534607
height = table._approx_text_height()
535608

536609
# Add the cells
Loading

lib/matplotlib/tests/test_table.py

+42
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
import numpy as np
88
from matplotlib.testing.decorators import image_comparison
99

10+
from matplotlib.table import CustomCell
11+
from matplotlib.path import Path
12+
from nose.tools import assert_equal
13+
1014

1115
@image_comparison(baseline_images=['table_zorder'],
1216
extensions=['png'],
@@ -79,3 +83,41 @@ def test_label_colours():
7983
colColours=colours,
8084
colLabels=['Header'] * dim,
8185
loc='best')
86+
87+
88+
@image_comparison(baseline_images=['table_cell_manipulation'],
89+
extensions=['png'], remove_text=True)
90+
def test_diff_cell_table():
91+
cells = ('horizontal', 'vertical', 'open', 'closed', 'T', 'R', 'B', 'L')
92+
cellText = [['1'] * len(cells)] * 2
93+
colWidths = [0.1] * len(cells)
94+
95+
_, axes = plt.subplots(nrows=len(cells), figsize=(4, len(cells)+1))
96+
for ax, cell in zip(axes, cells):
97+
ax.table(
98+
colWidths=colWidths,
99+
cellText=cellText,
100+
loc='center',
101+
edges=cell,
102+
)
103+
ax.axis('off')
104+
plt.tight_layout()
105+
106+
107+
def test_customcell():
108+
types = ('horizontal', 'vertical', 'open', 'closed', 'T', 'R', 'B', 'L')
109+
codes = (
110+
(Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO, Path.MOVETO),
111+
(Path.MOVETO, Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO),
112+
(Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.MOVETO),
113+
(Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY),
114+
(Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.LINETO, Path.MOVETO),
115+
(Path.MOVETO, Path.MOVETO, Path.LINETO, Path.MOVETO, Path.MOVETO),
116+
(Path.MOVETO, Path.LINETO, Path.MOVETO, Path.MOVETO, Path.MOVETO),
117+
(Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.LINETO),
118+
)
119+
120+
for t, c in zip(types, codes):
121+
cell = CustomCell((0, 0), visible_edges=t, width=1, height=1)
122+
code = tuple(s for _, s in cell.get_path().iter_segments())
123+
assert_equal(c, code)

0 commit comments

Comments
 (0)