Skip to content

Commit

Permalink
Merge pull request #4259 from dkua/feature_4044
Browse files Browse the repository at this point in the history
ENH : Make cell edges in Table configurable

Closes #4044
  • Loading branch information
tacaswell committed Apr 19, 2015
2 parents be216b2 + 17576d8 commit 62cce14
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 12 deletions.
11 changes: 11 additions & 0 deletions doc/users/whats_new/updated_table.rst
@@ -0,0 +1,11 @@
Updated Table and to control edge visibility
--------------------------------------------
Added the ability to toggle the visibility of lines in Tables.
Functionality added to the table() factory function under the keyword argument "edges".
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.

Example:
table(..., edges="open") # No line visible
table(..., edges="closed") # All lines visible
table(..., edges="horizontal") # Only top and bottom lines visible
table(..., edges="LT") # Only left and top lines visible.
97 changes: 85 additions & 12 deletions lib/matplotlib/table.py
Expand Up @@ -34,6 +34,7 @@
from matplotlib import docstring
from .text import Text
from .transforms import Bbox
from matplotlib.path import Path


class Cell(Rectangle):
Expand Down Expand Up @@ -146,6 +147,67 @@ def set_text_props(self, **kwargs):
self._text.update(kwargs)


class CustomCell(Cell):
"""
A subclass of Cell where the sides may be visibly toggled.
"""

_edges = 'BRTL'
_edge_aliases = {'open': '',
'closed': _edges, # default
'horizontal': 'BT',
'vertical': 'RL'
}

def __init__(self, *args, **kwargs):
visible_edges = kwargs.pop('visible_edges')
Cell.__init__(self, *args, **kwargs)
self.visible_edges = visible_edges

@property
def visible_edges(self):
return self._visible_edges

@visible_edges.setter
def visible_edges(self, value):
if value is None:
self._visible_edges = self._edges
elif value in self._edge_aliases:
self._visible_edges = self._edge_aliases[value]
else:
for edge in value:
if edge not in self._edges:
msg = ('Invalid edge param {0}, must only be one of'
' {1} or string of {2}.').format(
value,
", ".join(self._edge_aliases.keys()),
", ".join(self._edges),
)
raise ValueError(msg)
self._visible_edges = value

def get_path(self):
'Return a path where the edges specificed by _visible_edges are drawn'

codes = [Path.MOVETO]

for edge in self._edges:
if edge in self._visible_edges:
codes.append(Path.LINETO)
else:
codes.append(Path.MOVETO)

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

return Path(
[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]],
codes,
readonly=True
)


class Table(Artist):
"""
Create a table of cells.
Expand Down Expand Up @@ -203,6 +265,7 @@ def __init__(self, ax, loc=None, bbox=None, **kwargs):

self._texts = []
self._cells = {}
self._edges = None
self._autoRows = []
self._autoColumns = []
self._autoFontsize = True
Expand All @@ -216,13 +279,21 @@ def add_cell(self, row, col, *args, **kwargs):
""" Add a cell to the table. """
xy = (0, 0)

cell = Cell(xy, *args, **kwargs)
cell = CustomCell(xy, visible_edges=self.edges, *args, **kwargs)
cell.set_figure(self.figure)
cell.set_transform(self.get_transform())

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

@property
def edges(self):
return self._edges

@edges.setter
def edges(self, value):
self._edges = value

def _approx_text_height(self):
return (self.FONTSIZE / 72.0 * self.figure.dpi /
self._axes.bbox.height * 1.2)
Expand All @@ -246,8 +317,8 @@ def draw(self, renderer):
keys.sort()
for key in keys:
self._cells[key].draw(renderer)
#for c in self._cells.itervalues():
# c.draw(renderer)
# for c in self._cells.itervalues():
# c.draw(renderer)
renderer.close_group('table')

def _get_grid_bbox(self, renderer):
Expand All @@ -273,8 +344,8 @@ def contains(self, mouseevent):
# doesn't have to bind to each one individually.
if self._cachedRenderer is not None:
boxes = [self._cells[pos].get_window_extent(self._cachedRenderer)
for pos in six.iterkeys(self._cells)
if pos[0] >= 0 and pos[1] >= 0]
for pos in six.iterkeys(self._cells)
if pos[0] >= 0 and pos[1] >= 0]
bbox = Bbox.union(boxes)
return bbox.contains(mouseevent.x, mouseevent.y), {}
else:
Expand Down Expand Up @@ -455,23 +526,24 @@ def get_celld(self):


def table(ax,
cellText=None, cellColours=None,
cellLoc='right', colWidths=None,
rowLabels=None, rowColours=None, rowLoc='left',
colLabels=None, colColours=None, colLoc='center',
loc='bottom', bbox=None,
**kwargs):
cellText=None, cellColours=None,
cellLoc='right', colWidths=None,
rowLabels=None, rowColours=None, rowLoc='left',
colLabels=None, colColours=None, colLoc='center',
loc='bottom', bbox=None, edges='closed',
**kwargs):
"""
TABLE(cellText=None, cellColours=None,
cellLoc='right', colWidths=None,
rowLabels=None, rowColours=None, rowLoc='left',
colLabels=None, colColours=None, colLoc='center',
loc='bottom', bbox=None)
loc='bottom', bbox=None, edges='closed')
Factory function to generate a Table instance.
Thanks to John Gill for providing the class and table.
"""

# Check we have some cellText
if cellText is None:
# assume just colours are needed
Expand Down Expand Up @@ -531,6 +603,7 @@ def table(ax,

# Now create the table
table = Table(ax, loc, bbox, **kwargs)
table.edges = edges
height = table._approx_text_height()

# Add the cells
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions lib/matplotlib/tests/test_table.py
Expand Up @@ -7,6 +7,10 @@
import numpy as np
from matplotlib.testing.decorators import image_comparison

from matplotlib.table import CustomCell
from matplotlib.path import Path
from nose.tools import assert_equal


@image_comparison(baseline_images=['table_zorder'],
extensions=['png'],
Expand Down Expand Up @@ -79,3 +83,41 @@ def test_label_colours():
colColours=colours,
colLabels=['Header'] * dim,
loc='best')


@image_comparison(baseline_images=['table_cell_manipulation'],
extensions=['png'], remove_text=True)
def test_diff_cell_table():
cells = ('horizontal', 'vertical', 'open', 'closed', 'T', 'R', 'B', 'L')
cellText = [['1'] * len(cells)] * 2
colWidths = [0.1] * len(cells)

_, axes = plt.subplots(nrows=len(cells), figsize=(4, len(cells)+1))
for ax, cell in zip(axes, cells):
ax.table(
colWidths=colWidths,
cellText=cellText,
loc='center',
edges=cell,
)
ax.axis('off')
plt.tight_layout()


def test_customcell():
types = ('horizontal', 'vertical', 'open', 'closed', 'T', 'R', 'B', 'L')
codes = (
(Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO, Path.MOVETO),
(Path.MOVETO, Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO),
(Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.MOVETO),
(Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY),
(Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.LINETO, Path.MOVETO),
(Path.MOVETO, Path.MOVETO, Path.LINETO, Path.MOVETO, Path.MOVETO),
(Path.MOVETO, Path.LINETO, Path.MOVETO, Path.MOVETO, Path.MOVETO),
(Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.LINETO),
)

for t, c in zip(types, codes):
cell = CustomCell((0, 0), visible_edges=t, width=1, height=1)
code = tuple(s for _, s in cell.get_path().iter_segments())
assert_equal(c, code)

0 comments on commit 62cce14

Please sign in to comment.