pytermgui.widgets.pixel_matrix

The module containing all the widgets that can be used to display pixel-based data.

  1"""
  2The module containing all the widgets that can be used to display
  3pixel-based data.
  4"""
  5
  6from __future__ import annotations
  7
  8from ..ansi_interface import MouseEvent
  9from ..markup import tim
 10from ..regex import real_length
 11from .base import Widget
 12
 13__all__ = [
 14    "PixelMatrix",
 15    "DensePixelMatrix",
 16]
 17
 18
 19class PixelMatrix(Widget):
 20    """A matrix of pixels.
 21
 22    The way this object should be used is by accessing & modifying
 23    the underlying matrix. This can be done using the set & getitem
 24    syntacies:
 25
 26    ```python3
 27    from pytermgui import PixelMatrix
 28
 29    matrix = PixelMatrix(10, 10, default="white")
 30    for y in matrix.rows:
 31        for x in matrix.columns:
 32            matrix[y, x] = "black"
 33    ```
 34
 35    The above snippet draws a black diagonal going from the top left
 36    to bottom right.
 37
 38    Each item of the rows should be a single PyTermGUI-parsable color
 39    string. For more information about this, see
 40    `pytermgui.ansi_interface.Color`.
 41    """
 42
 43    selected_pixel: tuple[tuple[int, int], str] | None
 44    """A tuple of the position & value (color) of the currently hovered pixel."""
 45
 46    def __init__(self, width: int, height: int, default: str = "", **attrs) -> None:
 47        """Initializes a PixelMatrix.
 48
 49        Args:
 50            width: The amount of columns the matrix will have.
 51            height: The amount of rows the matrix will have.
 52            default: The default color to use to initialize the matrix with.
 53        """
 54
 55        super().__init__(**attrs)
 56
 57        self.rows = height
 58        self.columns = width
 59
 60        self._matrix = []
 61
 62        for _ in range(self.rows):
 63            self._matrix.append([default] * self.columns)
 64
 65        self.selected_pixel = None
 66        self.build()
 67
 68    @classmethod
 69    def from_matrix(cls, matrix: list[list[str]]) -> PixelMatrix:
 70        """Creates a PixelMatrix from the given matrix.
 71
 72        The given matrix should be a list of rows, each containing a number
 73        of cells. It is optimal for all rows to share the same amount of cells.
 74
 75        Args:
 76            matrix: The matrix to use. This is a list of lists of strings
 77                with each element representing a PyTermGUI-parseable color.
 78
 79        Returns:
 80            A new type(self).
 81        """
 82
 83        obj = cls(max(len(row) for row in matrix), len(matrix))
 84        setattr(obj, "_matrix", matrix)
 85        obj.build()
 86
 87        return obj
 88
 89    def _update_dimensions(self, lines: list[str]):
 90        """Updates the dimensions of this matrix.
 91
 92        Args:
 93            lines: A list of lines that the calculations will be based upon.
 94        """
 95
 96        self.static_width = max(real_length(line) for line in lines)
 97        self.height = len(lines)
 98
 99    def on_hover(self, event: MouseEvent) -> bool:
100        """Sets `selected_pixel` to the current pixel."""
101
102        xoffset = event.position[0] - self.pos[0]
103        yoffset = event.position[1] - self.pos[1]
104
105        color = self._matrix[yoffset][xoffset // 2]
106
107        self.selected_pixel = ((xoffset // 2, yoffset), color)
108        return True
109
110    def get_lines(self) -> list[str]:
111        """Returns lines built by the `build` method."""
112
113        return self._lines
114
115    def build(self) -> list[str]:
116        """Builds the image pixels.
117
118        Returns:
119            The lines that this object will return, until a subsequent `build` call.
120            These lines are stored in the `self._lines` variable.
121        """
122
123        lines: list[str] = []
124        for row in self._matrix:
125            line = ""
126            for pixel in row:
127                if len(pixel) > 0:
128                    line += f"[@{pixel}]  "
129                else:
130                    line += "[/]  "
131
132            lines.append(tim.parse(line))
133
134        self._lines = lines
135        self._update_dimensions(lines)
136
137        return lines
138
139    def __getitem__(self, indices: tuple[int, int]) -> str:
140        """Gets a matrix item."""
141
142        posy, posx = indices
143        return self._matrix[posy][posx]
144
145    def __setitem__(self, indices: tuple[int, int], value: str) -> None:
146        """Sets a matrix item."""
147
148        posy, posx = indices
149        self._matrix[posy][posx] = value
150
151
152class DensePixelMatrix(PixelMatrix):
153    """A more dense (2x) PixelMatrix.
154
155    Due to each pixel only occupying 1/2 characters in height, accurately
156    determining selected_pixel is impossible, thus the functionality does
157    not exist here.
158    """
159
160    def __init__(self, width: int, height: int, default: str = "", **attrs) -> None:
161        """Initializes DensePixelMatrix.
162
163        Args:
164            width: The width of the matrix.
165            height: The height of the matrix.
166            default: The default color to use to initialize the matrix with.
167        """
168
169        super().__init__(width, height, default, **attrs)
170
171        self.width = width // 2
172
173    def handle_mouse(self, event: MouseEvent) -> bool:
174        """As mentioned in the class documentation, mouse handling is disabled here."""
175
176        return False
177
178    def build(self) -> list[str]:
179        """Builds the image pixels, using half-block characters.
180
181        Returns:
182            The lines that this object will return, until a subsequent `build` call.
183            These lines are stored in the `self._lines` variable.
184        """
185
186        lines = []
187        lines_to_zip: list[list[str]] = []
188        for row in self._matrix:
189            lines_to_zip.append(row)
190            if len(lines_to_zip) != 2:
191                continue
192
193            line = ""
194            top_row, bottom_row = lines_to_zip[0], lines_to_zip[1]
195            for bottom, top in zip(bottom_row, top_row):
196                if len(top) + len(bottom) == 0:
197                    line += " "
198                    continue
199
200                if bottom == "":
201                    line += tim.parse(f"[{top}]▀")
202                    continue
203
204                markup_str = "@" + top + " " if len(top) > 0 else ""
205
206                markup_str += bottom
207                line += tim.parse(f"[{markup_str}]▄")
208
209            lines.append(line)
210            lines_to_zip = []
211
212        self._lines = lines
213        self._update_dimensions(lines)
214
215        return lines
class PixelMatrix(pytermgui.widgets.base.Widget):
 20class PixelMatrix(Widget):
 21    """A matrix of pixels.
 22
 23    The way this object should be used is by accessing & modifying
 24    the underlying matrix. This can be done using the set & getitem
 25    syntacies:
 26
 27    ```python3
 28    from pytermgui import PixelMatrix
 29
 30    matrix = PixelMatrix(10, 10, default="white")
 31    for y in matrix.rows:
 32        for x in matrix.columns:
 33            matrix[y, x] = "black"
 34    ```
 35
 36    The above snippet draws a black diagonal going from the top left
 37    to bottom right.
 38
 39    Each item of the rows should be a single PyTermGUI-parsable color
 40    string. For more information about this, see
 41    `pytermgui.ansi_interface.Color`.
 42    """
 43
 44    selected_pixel: tuple[tuple[int, int], str] | None
 45    """A tuple of the position & value (color) of the currently hovered pixel."""
 46
 47    def __init__(self, width: int, height: int, default: str = "", **attrs) -> None:
 48        """Initializes a PixelMatrix.
 49
 50        Args:
 51            width: The amount of columns the matrix will have.
 52            height: The amount of rows the matrix will have.
 53            default: The default color to use to initialize the matrix with.
 54        """
 55
 56        super().__init__(**attrs)
 57
 58        self.rows = height
 59        self.columns = width
 60
 61        self._matrix = []
 62
 63        for _ in range(self.rows):
 64            self._matrix.append([default] * self.columns)
 65
 66        self.selected_pixel = None
 67        self.build()
 68
 69    @classmethod
 70    def from_matrix(cls, matrix: list[list[str]]) -> PixelMatrix:
 71        """Creates a PixelMatrix from the given matrix.
 72
 73        The given matrix should be a list of rows, each containing a number
 74        of cells. It is optimal for all rows to share the same amount of cells.
 75
 76        Args:
 77            matrix: The matrix to use. This is a list of lists of strings
 78                with each element representing a PyTermGUI-parseable color.
 79
 80        Returns:
 81            A new type(self).
 82        """
 83
 84        obj = cls(max(len(row) for row in matrix), len(matrix))
 85        setattr(obj, "_matrix", matrix)
 86        obj.build()
 87
 88        return obj
 89
 90    def _update_dimensions(self, lines: list[str]):
 91        """Updates the dimensions of this matrix.
 92
 93        Args:
 94            lines: A list of lines that the calculations will be based upon.
 95        """
 96
 97        self.static_width = max(real_length(line) for line in lines)
 98        self.height = len(lines)
 99
100    def on_hover(self, event: MouseEvent) -> bool:
101        """Sets `selected_pixel` to the current pixel."""
102
103        xoffset = event.position[0] - self.pos[0]
104        yoffset = event.position[1] - self.pos[1]
105
106        color = self._matrix[yoffset][xoffset // 2]
107
108        self.selected_pixel = ((xoffset // 2, yoffset), color)
109        return True
110
111    def get_lines(self) -> list[str]:
112        """Returns lines built by the `build` method."""
113
114        return self._lines
115
116    def build(self) -> list[str]:
117        """Builds the image pixels.
118
119        Returns:
120            The lines that this object will return, until a subsequent `build` call.
121            These lines are stored in the `self._lines` variable.
122        """
123
124        lines: list[str] = []
125        for row in self._matrix:
126            line = ""
127            for pixel in row:
128                if len(pixel) > 0:
129                    line += f"[@{pixel}]  "
130                else:
131                    line += "[/]  "
132
133            lines.append(tim.parse(line))
134
135        self._lines = lines
136        self._update_dimensions(lines)
137
138        return lines
139
140    def __getitem__(self, indices: tuple[int, int]) -> str:
141        """Gets a matrix item."""
142
143        posy, posx = indices
144        return self._matrix[posy][posx]
145
146    def __setitem__(self, indices: tuple[int, int], value: str) -> None:
147        """Sets a matrix item."""
148
149        posy, posx = indices
150        self._matrix[posy][posx] = value

A matrix of pixels.

The way this object should be used is by accessing & modifying the underlying matrix. This can be done using the set & getitem syntacies:

from pytermgui import PixelMatrix

matrix = PixelMatrix(10, 10, default="white")
for y in matrix.rows:
    for x in matrix.columns:
        matrix[y, x] = "black"

The above snippet draws a black diagonal going from the top left to bottom right.

Each item of the rows should be a single PyTermGUI-parsable color string. For more information about this, see pytermgui.ansi_interface.Color.

PixelMatrix(width: int, height: int, default: str = '', **attrs)
47    def __init__(self, width: int, height: int, default: str = "", **attrs) -> None:
48        """Initializes a PixelMatrix.
49
50        Args:
51            width: The amount of columns the matrix will have.
52            height: The amount of rows the matrix will have.
53            default: The default color to use to initialize the matrix with.
54        """
55
56        super().__init__(**attrs)
57
58        self.rows = height
59        self.columns = width
60
61        self._matrix = []
62
63        for _ in range(self.rows):
64            self._matrix.append([default] * self.columns)
65
66        self.selected_pixel = None
67        self.build()

Initializes a PixelMatrix.

Args
  • width: The amount of columns the matrix will have.
  • height: The amount of rows the matrix will have.
  • default: The default color to use to initialize the matrix with.
selected_pixel: tuple[tuple[int, int], str] | None

A tuple of the position & value (color) of the currently hovered pixel.

@classmethod
def from_matrix( cls, matrix: list[list[str]]) -> pytermgui.widgets.pixel_matrix.PixelMatrix:
69    @classmethod
70    def from_matrix(cls, matrix: list[list[str]]) -> PixelMatrix:
71        """Creates a PixelMatrix from the given matrix.
72
73        The given matrix should be a list of rows, each containing a number
74        of cells. It is optimal for all rows to share the same amount of cells.
75
76        Args:
77            matrix: The matrix to use. This is a list of lists of strings
78                with each element representing a PyTermGUI-parseable color.
79
80        Returns:
81            A new type(self).
82        """
83
84        obj = cls(max(len(row) for row in matrix), len(matrix))
85        setattr(obj, "_matrix", matrix)
86        obj.build()
87
88        return obj

Creates a PixelMatrix from the given matrix.

The given matrix should be a list of rows, each containing a number of cells. It is optimal for all rows to share the same amount of cells.

Args
  • matrix: The matrix to use. This is a list of lists of strings with each element representing a PyTermGUI-parseable color.
Returns

A new type(self).

def on_hover(self, event: pytermgui.ansi_interface.MouseEvent) -> bool:
100    def on_hover(self, event: MouseEvent) -> bool:
101        """Sets `selected_pixel` to the current pixel."""
102
103        xoffset = event.position[0] - self.pos[0]
104        yoffset = event.position[1] - self.pos[1]
105
106        color = self._matrix[yoffset][xoffset // 2]
107
108        self.selected_pixel = ((xoffset // 2, yoffset), color)
109        return True

Sets selected_pixel to the current pixel.

def get_lines(self) -> list[str]:
111    def get_lines(self) -> list[str]:
112        """Returns lines built by the `build` method."""
113
114        return self._lines

Returns lines built by the build method.

def build(self) -> list[str]:
116    def build(self) -> list[str]:
117        """Builds the image pixels.
118
119        Returns:
120            The lines that this object will return, until a subsequent `build` call.
121            These lines are stored in the `self._lines` variable.
122        """
123
124        lines: list[str] = []
125        for row in self._matrix:
126            line = ""
127            for pixel in row:
128                if len(pixel) > 0:
129                    line += f"[@{pixel}]  "
130                else:
131                    line += "[/]  "
132
133            lines.append(tim.parse(line))
134
135        self._lines = lines
136        self._update_dimensions(lines)
137
138        return lines

Builds the image pixels.

Returns

The lines that this object will return, until a subsequent build call. These lines are stored in the self._lines variable.

class DensePixelMatrix(PixelMatrix):
153class DensePixelMatrix(PixelMatrix):
154    """A more dense (2x) PixelMatrix.
155
156    Due to each pixel only occupying 1/2 characters in height, accurately
157    determining selected_pixel is impossible, thus the functionality does
158    not exist here.
159    """
160
161    def __init__(self, width: int, height: int, default: str = "", **attrs) -> None:
162        """Initializes DensePixelMatrix.
163
164        Args:
165            width: The width of the matrix.
166            height: The height of the matrix.
167            default: The default color to use to initialize the matrix with.
168        """
169
170        super().__init__(width, height, default, **attrs)
171
172        self.width = width // 2
173
174    def handle_mouse(self, event: MouseEvent) -> bool:
175        """As mentioned in the class documentation, mouse handling is disabled here."""
176
177        return False
178
179    def build(self) -> list[str]:
180        """Builds the image pixels, using half-block characters.
181
182        Returns:
183            The lines that this object will return, until a subsequent `build` call.
184            These lines are stored in the `self._lines` variable.
185        """
186
187        lines = []
188        lines_to_zip: list[list[str]] = []
189        for row in self._matrix:
190            lines_to_zip.append(row)
191            if len(lines_to_zip) != 2:
192                continue
193
194            line = ""
195            top_row, bottom_row = lines_to_zip[0], lines_to_zip[1]
196            for bottom, top in zip(bottom_row, top_row):
197                if len(top) + len(bottom) == 0:
198                    line += " "
199                    continue
200
201                if bottom == "":
202                    line += tim.parse(f"[{top}]▀")
203                    continue
204
205                markup_str = "@" + top + " " if len(top) > 0 else ""
206
207                markup_str += bottom
208                line += tim.parse(f"[{markup_str}]▄")
209
210            lines.append(line)
211            lines_to_zip = []
212
213        self._lines = lines
214        self._update_dimensions(lines)
215
216        return lines

A more dense (2x) PixelMatrix.

Due to each pixel only occupying 1/2 characters in height, accurately determining selected_pixel is impossible, thus the functionality does not exist here.

DensePixelMatrix(width: int, height: int, default: str = '', **attrs)
161    def __init__(self, width: int, height: int, default: str = "", **attrs) -> None:
162        """Initializes DensePixelMatrix.
163
164        Args:
165            width: The width of the matrix.
166            height: The height of the matrix.
167            default: The default color to use to initialize the matrix with.
168        """
169
170        super().__init__(width, height, default, **attrs)
171
172        self.width = width // 2

Initializes DensePixelMatrix.

Args
  • width: The width of the matrix.
  • height: The height of the matrix.
  • default: The default color to use to initialize the matrix with.
def handle_mouse(self, event: pytermgui.ansi_interface.MouseEvent) -> bool:
174    def handle_mouse(self, event: MouseEvent) -> bool:
175        """As mentioned in the class documentation, mouse handling is disabled here."""
176
177        return False

As mentioned in the class documentation, mouse handling is disabled here.

def build(self) -> list[str]:
179    def build(self) -> list[str]:
180        """Builds the image pixels, using half-block characters.
181
182        Returns:
183            The lines that this object will return, until a subsequent `build` call.
184            These lines are stored in the `self._lines` variable.
185        """
186
187        lines = []
188        lines_to_zip: list[list[str]] = []
189        for row in self._matrix:
190            lines_to_zip.append(row)
191            if len(lines_to_zip) != 2:
192                continue
193
194            line = ""
195            top_row, bottom_row = lines_to_zip[0], lines_to_zip[1]
196            for bottom, top in zip(bottom_row, top_row):
197                if len(top) + len(bottom) == 0:
198                    line += " "
199                    continue
200
201                if bottom == "":
202                    line += tim.parse(f"[{top}]▀")
203                    continue
204
205                markup_str = "@" + top + " " if len(top) > 0 else ""
206
207                markup_str += bottom
208                line += tim.parse(f"[{markup_str}]▄")
209
210            lines.append(line)
211            lines_to_zip = []
212
213        self._lines = lines
214        self._update_dimensions(lines)
215
216        return lines

Builds the image pixels, using half-block characters.

Returns

The lines that this object will return, until a subsequent build call. These lines are stored in the self._lines variable.