Source code for manim_beanim.graphing_objects.table_general

from ..my_imports import *
__all__ = ["Table"]


[docs] class Table(VGroup): """ Simplify creation and decoration of tables in Manim, with optional dictionary loading and styling. This class supports manual content input (a list of table rows) or dictionary-based loading of structured content (e.g., mathematical expressions). It offers extensive customization options, including color themes, corner radii, decorative borders, and row highlighting. :param content: Table content as a list of rows (each row is a list of elements), or a dictionary key. :type content: list[list] or str :param dictionary: Optional path to a dictionary file for loading content. If ``"data_base"``, uses the built-in Beanim table dictionary. :type dictionary: str, optional :param highlight_top: Whether to highlight the top row. Use ``"yes"`` (default) to enable. :type highlight_top: str :param text_size: Font size for each table cell. Default is ``55``. :type text_size: float :param text_color: Color of text in the table. Default is ``WHITE``. :type text_color: ParsableManimColor :param decorator_presence: Type of decoration to apply. Use ``"box"`` to wrap content in a frame. :type decorator_presence: str :param decorator_color: Primary color for the decorator. Default is ``WHITE``. :type decorator_color: ParsableManimColor :param decorator_color_2: Highlight color for top row cell 1. Default is ``RED``. :type decorator_color_2: ParsableManimColor :param decorator_color_3: Highlight color for top row cell 2. Default is ``BLUE``. :type decorator_color_3: ParsableManimColor :param decorator_color_4: Highlight color for top row cell 3. Default is ``GREEN``. :type decorator_color_4: ParsableManimColor :param decorator_stroke_width: Stroke width for decorator borders. Default is ``1``. :type decorator_stroke_width: float :param corner_rad: Corner radius for decorator boxes. Default is ``0.1``. :type corner_rad: float :param corner_rad_direction: List of four ints controlling which corners are rounded: [top-left, top-right, bottom-right, bottom-left]. Default is ``[1, 1, 1, 1]``. :type corner_rad_direction: list[int] :param fill_opa: Fill opacity for decorative backgrounds. Default is ``0.1``. :type fill_opa: float :param h_tightness: Horizontal padding inside table cells. Default is ``0.5``. :type h_tightness: float :param v_tightness: Vertical padding inside table cells. Default is ``0.5``. :type v_tightness: float :param tightness: Padding around the decorator box. Default is ``0``. :type tightness: float :param stroke_opa: Opacity of table/border strokes. Default is ``1``. :type stroke_opa: float :param kwargs: Additional keyword arguments passed to :class:`VGroup`. .. note:: When using dictionary content, the dictionary file should map keys to row data lists. See :meth:`handle_with_dictionary` for details. **Example usage:** .. code-block:: python from manim import * from manim_beanim import Table class Example_Table(Scene): def construct(self): table1 = Table(content=[["1", "2"], ["3", "4"]]) table2 = Table(content="table_key", dictionary="data_base") self.add(VGroup(table1, table2).arrange(DOWN)) """ def __init__( self, content=None, dictionary=None, highlight_top="yes", text_size: float = 55, text_color: ParsableManimColor = WHITE, decorator_presence: str = "box", decorator_color: ParsableManimColor = WHITE, decorator_color_2: ParsableManimColor = RED, decorator_color_3: ParsableManimColor = BLUE, decorator_color_4: ParsableManimColor = GREEN, decorator_stroke_width: float = 1, corner_rad: float = 0.1, corner_rad_direction: list = [1, 1, 1, 1], fill_opa: float = 0.1, h_tightness: float = 0.5, v_tightness: float = 0.5, tightness: float = 0, stroke_opa: float = 1, **kwargs ): super().__init__(**kwargs) self.content = content self.dictionary = dictionary self.highlight_top = highlight_top self.text_size = text_size self.text_color = text_color self.decorator_presence = decorator_presence self.decorator_color = decorator_color self.decorator_color_2 = decorator_color_2 self.decorator_color_3 = decorator_color_3 self.decorator_color_4 = decorator_color_4 self.decorator_stroke_width = decorator_stroke_width self.corner_rad = corner_rad self.corner_rad_all = list(corner_rad * np.array(corner_rad_direction)) self.crad = corner_rad # Special for box with title self.fill_opa = fill_opa self.h_tightness = h_tightness self.v_tightness = v_tightness self.tightness = tightness self.stroke_opa = stroke_opa if self.dictionary is None: self.handle_no_dictionary() else: self.handle_with_dictionary("tables")
[docs] def handle_no_dictionary(self): """ Render table directly from manually provided content. Creates a :class:`MathTable` from the provided content list and applies decoration. **Calls:** - :meth:`add_decorator` """ self.chosen_content = MathTable( self.content, line_config={"stroke_width": self.decorator_stroke_width, "color": self.text_color}, include_outer_lines=False, v_buff=self.v_tightness, h_buff=self.h_tightness, stroke_opacity=self.stroke_opa, ).set(color=self.text_color) self.add_decorator(self.chosen_content)
[docs] def handle_with_dictionary(self, dic_in_data_base): """ Render table by loading content from a dictionary file. If ``dictionary == "data_base"``, uses the built-in table dictionary. Otherwise loads from the provided path, splits directory and filename, and then constructs a :class:`MathTable` from the retrieved data. :param dic_in_data_base: Base name for built-in dictionary files. :type dic_in_data_base: str """ if self.dictionary == "data_base": self.dictionary = path.join( path.dirname(__file__), "dictionary_graphing_objects/" + dic_in_data_base + ".txt" ) my_path, my_file = self.split_dictionary_path(self.dictionary) if not self.check_file_exists(my_path, my_file): print("Dictionary file not found; generating manually may be required.") chosen_dic = self.load_dictionary() data = chosen_dic[self.content] self.chosen_content = MathTable( data, line_config={"stroke_width": self.decorator_stroke_width, "color": self.text_color}, include_outer_lines=False, v_buff=self.v_tightness, h_buff=self.h_tightness, stroke_opacity=self.stroke_opa, ).set(color=self.text_color) self.add_decorator(self.chosen_content)
[docs] def load_dictionary(self): """ Load and parse a dictionary file containing table data. :return: Parsed dictionary mapping content keys to table rows. :rtype: dict """ with open(self.dictionary, mode='r') as f: text = f.read() return ast.literal_eval(text)
[docs] def add_highlight_top_row(self): """ Highlight the top row of the table using specified decorator colors. """ row = self.filter_type() colors = [self.decorator_color_2, self.decorator_color_3, self.decorator_color_4] for i in range(1, len(row) + 1): corner = [1, 0, 0, 0] if i == 1 else ([0, 0, 0, 1] if i == len(row) else [0, 0, 0, 0]) self.chosen_content.add_highlighted_cell((1, i), color=colors[i % len( colors)], corner_radius=list(self.corner_rad * np.array(corner)))
[docs] def filter_type(self): """ Determine the top row data regardless of content type. :return: The first row of the table content. :rtype: list """ if isinstance(self.content, str): return self.load_dictionary()[self.content][0] return self.content[0]
[docs] def add_decorator(self, mobject): """ Add decorators (highlight row and optional box) around the table. This method first calls :meth:`add_highlight_top_row`, then wraps the table in a box if ``decorator_presence == "box"``. """ self.add_highlight_top_row() if self.decorator_presence == "box": box = SurroundingRectangle( mobject, corner_radius=self.corner_rad_all, buff=4*self.tightness, stroke_width=self.decorator_stroke_width, color=self.decorator_color, fill_opacity=self.fill_opa, stroke_opacity=self.stroke_opa ) self.add(mobject, box) else: self.add(mobject)
[docs] def split_dictionary_path(self, input_string): """ Split a path string at the last slash into directory and filename. :param input_string: The full path to split. :type input_string: str :return: [directory, filename] :rtype: list[str] **Example:** .. code-block:: python >>> self.split_dictionary_path("path/to/file.txt") ['path/to', 'file.txt'] """ return input_string.rsplit("/", 1)
[docs] def check_file_exists(self, directory, filename): """ Check if a file exists in the given directory. :param directory: Directory path to check. :type directory: str or Path :param filename: Filename to look for. :type filename: str :return: True if the file exists, False otherwise. :rtype: bool **Example:** .. code-block:: python >>> self.check_file_exists("path/to/dir", "file.txt") True """ file_path = Path(directory) / filename return file_path.is_file()