Skip to content

boxes

Convenience objects for Container corner & border styles.

They can be used as:

from pytermgui import Container, boxes

boxes.DOUBLE_TOP.set_chars_of(Container)
c = Container() # this will now use the style chosen

Boxes are also settable as a property of pytermgui.widgets.Container, and can be referenced & defined in markup file definitions. For more info, check out pytermgui.file_loaders.

Box

Class for defining border & corner styles

lines should be list[str] of length 3, such as:

lines = [
    ".---.",
    "| x |",
    "`---`",
]

The length of individual lines is arbitrary, only limitation is that the top & bottom border characters should occur most often in their respective lines.

You can set corners to be of any length, their end is calculated by finding the index of the most often occuring character, which is assumed to be the border character.

Top & bottom borders are currently limited in length to 1, but sides operate similarly to corners. They are separated by finding the index of the fill char from the start or end. The content char is "x" by default, however it can be set to anything else by giving the "content_char" construction parameter.

As such, this:

boxes.Box(
   [
       "corner1 ________________ corner2",
       "xleft   ################ rightxx",
       "corner3 ---------------- corner4",
   ],
   content_char="#",
)

Will result in:

Box(
    borders=['xleft   ', '_', ' rightxx', '-'],
    corners=['corner1 ', ' corner2', ' corner4', 'corner3 ']
)
Source code in pytermgui/widgets/boxes.py
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
class Box:
    """Class for defining border & corner styles

    `lines` should be `list[str]` of length 3, such as:

    ```python3
    lines = [
        ".---.",
        "| x |",
        "`---`",
    ]
    ```

    The length of individual lines is arbitrary, only limitation is
    that the top & bottom border characters should occur most often in
    their respective lines.

    You can set corners to be of any length, their end is calculated by
    finding the index of the most often occuring character, which is assumed
    to be the border character.

    Top & bottom borders are currently limited in length to 1, but sides
    operate similarly to corners. They are separated by finding the index
    of the fill char from the start or end. The content char is "x" by
    default, however it can be set to anything else by giving the "content_char"
    construction parameter.

    As such, this:

    ```python3
    boxes.Box(
       [
           "corner1 ________________ corner2",
           "xleft   ################ rightxx",
           "corner3 ---------------- corner4",
       ],
       content_char="#",
    )
    ```

    Will result in:

    ```python3
    Box(
        borders=['xleft   ', '_', ' rightxx', '-'],
        corners=['corner1 ', ' corner2', ' corner4', 'corner3 ']
    )
    ```
    """

    CharType = Tuple[str, str, str, str]

    def __init__(self, lines: list[str], content_char: str = "x"):
        """Set instance attributes"""

        super().__init__()
        self.content_char = content_char

        top, _, bottom = lines
        top_left, top_right = self._get_corners(top)
        bottom_left, bottom_right = self._get_corners(bottom)

        self.borders = list(self._get_borders(lines))
        self.corners = [
            top_left,
            top_right,
            bottom_right,
            bottom_left,
        ]

    def __repr__(self) -> str:
        """Return string of self"""

        return self.debug()

    @staticmethod
    def _find_mode_char(line: str) -> str:
        """Find most often consecutively occuring character in string"""

        instances = 0
        current_char = ""

        results: list[tuple[str, int]] = []
        for char in line:
            if current_char == char:
                instances += 1
            else:
                if len(current_char) > 0:
                    results.append((current_char, instances))

                instances = 1
                current_char = char

        results.append((current_char, instances))

        results.sort(key=lambda item: item[1])
        if len(results) == 0:
            print(line, instances, current_char)

        return results[-1][0]

    def _get_corners(self, line: str) -> tuple[str, str]:
        """Get corners from a line"""

        mode_char = self._find_mode_char(line)
        left = line[: line.index(mode_char)]
        right = line[real_length(line) - (line[::-1].index(mode_char)) :]

        return left, right

    def _get_borders(self, lines: list[str]) -> tuple[str, str, str, str]:
        """Get borders from all lines"""

        top, middle, bottom = lines
        middle_reversed = middle[::-1]

        top_border = self._find_mode_char(top)
        left_border = middle[: middle.index(self.content_char)]

        right_border = middle[
            real_length(middle) - middle_reversed.index(self.content_char) :
        ]
        bottom_border = self._find_mode_char(bottom)

        # return top_border, left_border, right_border, bottom_border
        return left_border, top_border, right_border, bottom_border

    def set_chars_of(self, cls_or_obj: WidgetType) -> WidgetType:
        """Set border & corner chars of cls_or_obj to self values"""

        # We cannot import any widgets into here due to cyclic imports,
        # so we have to "hack" around it.
        if not hasattr(cls_or_obj, "set_char"):
            raise NotImplementedError(
                f"Object of type {cls_or_obj} does not support `set_char`"
            )

        cls_or_obj.set_char("border", self.borders)
        cls_or_obj.set_char("corner", self.corners)

        return cls_or_obj

    def debug(self) -> str:
        """Return identifiable information about object"""

        return f"Box(borders={self.borders}, corners={self.corners})"

__init__(lines, content_char='x')

Set instance attributes

Source code in pytermgui/widgets/boxes.py
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def __init__(self, lines: list[str], content_char: str = "x"):
    """Set instance attributes"""

    super().__init__()
    self.content_char = content_char

    top, _, bottom = lines
    top_left, top_right = self._get_corners(top)
    bottom_left, bottom_right = self._get_corners(bottom)

    self.borders = list(self._get_borders(lines))
    self.corners = [
        top_left,
        top_right,
        bottom_right,
        bottom_left,
    ]

__repr__()

Return string of self

Source code in pytermgui/widgets/boxes.py
95
96
97
98
def __repr__(self) -> str:
    """Return string of self"""

    return self.debug()

debug()

Return identifiable information about object

Source code in pytermgui/widgets/boxes.py
167
168
169
170
def debug(self) -> str:
    """Return identifiable information about object"""

    return f"Box(borders={self.borders}, corners={self.corners})"

set_chars_of(cls_or_obj)

Set border & corner chars of cls_or_obj to self values

Source code in pytermgui/widgets/boxes.py
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def set_chars_of(self, cls_or_obj: WidgetType) -> WidgetType:
    """Set border & corner chars of cls_or_obj to self values"""

    # We cannot import any widgets into here due to cyclic imports,
    # so we have to "hack" around it.
    if not hasattr(cls_or_obj, "set_char"):
        raise NotImplementedError(
            f"Object of type {cls_or_obj} does not support `set_char`"
        )

    cls_or_obj.set_char("border", self.borders)
    cls_or_obj.set_char("corner", self.corners)

    return cls_or_obj