pytermgui.widgets.pixel_matrix

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

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

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:
View Source
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

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 handle_mouse(self, event: pytermgui.ansi_interface.MouseEvent) -> bool:
View Source
 99    def handle_mouse(self, event: MouseEvent) -> bool:
100        """Handles a mouse event.
101
102        On hover, the `selected_pixel` attribute is set to the current pixel.
103        """
104
105        if event.action is MouseAction.HOVER:
106            xoffset = event.position[0] - self.pos[0]
107            yoffset = event.position[1] - self.pos[1]
108
109            color = self._matrix[yoffset][xoffset // 2]
110
111            self.selected_pixel = ((xoffset // 2, yoffset), color)
112            return True
113
114        return False

Handles a mouse event.

On hover, the selected_pixel attribute is set to the current pixel.

#   def get_lines(self) -> list[str]:
View Source
116    def get_lines(self) -> list[str]:
117        """Returns lines built by the `build` method."""
118
119        return self._lines

Returns lines built by the build method.

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

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

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