from typing import Sequence, Any, List, Tuple
from pyexlatex.presentation.beamer.columns.column import Column
from pyexlatex.presentation.beamer.columns.columns import Columns
from pyexlatex.layouts.base import LayoutBase
[docs]class CellLayout(LayoutBase):
"""
Lay content out in a grid where each cell is arbitrarily sized
"""
[docs] def __init__(self, content: Sequence[Sequence[Any]], grid_shape: Sequence[Sequence[Tuple[float, float]]], **kwargs):
"""
Args:
content:
grid_shape: list of list of tuples of (width, height), e.g. [
[(0.3, 0.4), (0.7, 0.4)],
[(0.3, 0.6), (0.7, 0.6)],
] would create a 2x2 grid with 40% of height in the first row, 60% in the second row. 30% width in the
first column, 70% width in the second column.
**kwargs:
"""
self.grid_shape = grid_shape
super().__init__(content, **kwargs)
self._validate()
self.content = self._get_layout()
def _validate(self):
value_error = ValueError(f'must pass content and grid_shape of the same dimensions, '
f'got content {self.contents} and grid_shape {self.grid_shape}')
if len(self.contents) != len(self.grid_shape):
raise value_error
for i, content_row in enumerate(self.contents):
shape_row = self.grid_shape[i]
if len(shape_row) != len(content_row):
raise value_error
def _get_layout(self) -> List[Columns]:
content_in_columns = []
for row_num, shape_row in enumerate(self.grid_shape):
cells = []
for col_num, (width, height) in enumerate(shape_row):
cell_content = self.contents[row_num][col_num]
cell = Column(
cell_content,
frac_of_text_width=self._rescale_width(width, len(shape_row)),
frac_of_page_height=self._rescale_height(height)
)
cells.append(cell)
row = Columns(cells)
content_in_columns.append(row)
return content_in_columns
@staticmethod
def _rescale_height(height: float):
"""
With the standard Madrid header and footer, 0.8\textheight is about the full area with a bit of padding on
top and bottom. Therefore passing 0.5 should be rescaled to 50% of the available area, or 0.4\textheight
"""
# TODO [#5]: better handling for beamer themes in CellLayout
#
# Available area will be different by theme.
available_area = 0.8
return height * available_area
@staticmethod
def _rescale_width(width: float, num_cols: int):
"""
Latex adds padding inbetween the columns, so 100% area is not really available. Need to set the available
area based on the number of columns
Args:
width:
num_cols:
Returns:
"""
# TODO [#6]: figure out how available area changes based on number of columns for CellLayout
#
# Defaulting to doing nothing now, assuming full area always in CellLayout
available_area = 1
return width * available_area
[docs]class GridLayout(CellLayout):
"""
Evenly spaced grid layout.
Creates a CellLayout, automatically setting widths and heights as an even split based on the shape
of the content passed to make a grid
"""
[docs] def __init__(self, content: Sequence[Sequence[Any]], **kwargs):
grid_shape = []
num_rows = len(content)
height = 1/num_rows
for row in content:
num_columns = len(row)
width = 1/num_columns
grid_shape.append([(width, height) for _ in range(len(row))])
super().__init__(content, grid_shape, **kwargs)