Skip to content

color_picker

The module containing the ColorPicker widget, as well as some helpers it needs.

To test out the widget, run ptg --color!

ColorPicker

Bases: Container

A simple ColorPicker widget.

This is used to visualize xterm-255 colors. RGB colors are not included here, as it is probably easier to use a web-based picker for those anyways.

Source code in pytermgui/widgets/color_picker.py
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
class ColorPicker(Container):
    """A simple ColorPicker widget.

    This is used to visualize xterm-255 colors. RGB colors are not
    included here, as it is probably easier to use a web-based picker
    for those anyways.
    """

    size_policy = SizePolicy.STATIC

    def __init__(self, show_output: bool = True, **attrs: Any) -> None:
        """Initializes a ColorPicker.

        Attrs:
            show_output: Decides whether the output Container should be
                added. If not set, the widget will only display the
                PixelMatrix of colors.
        """

        super().__init__(**attrs)
        self.show_output = show_output

        self._matrix = PixelMatrix.from_matrix(_get_xterm_matrix())

        self.width = 72
        self.box = boxes.EMPTY

        self._add_widget(self._matrix, run_get_lines=False)

        self.chosen = Joiner()
        self._output = Container(self.chosen, "", "", "")

        if self.show_output:
            self._add_widget(self._output)

    @property
    def selectables_length(self) -> int:
        """Returns either the button count or 1."""

        return max(super().selectables_length, 1)

    def handle_mouse(self, event: MouseEvent) -> bool:
        """Handles mouse events.

        On hover, the widget will display the currently hovered
        color and some testing text.

        On click, it will add a _FadeInButton for the currently
        hovered color.

        Args:
            event: The event to handle.
        """

        if super().handle_mouse(event):
            return True

        if not self.show_output or not self._matrix.contains(event.position):
            return False

        if event.action is MouseAction.LEFT_CLICK:
            if self._matrix.selected_pixel is None:
                return True

            _, color = self._matrix.selected_pixel
            if len(color) == 0:
                return False

            button = _FadeInButton(f"{color:^5}", width=5)
            button.styles.label = f"@{color}"
            self.chosen.lazy_add(button)

            return True

        return False

    def get_lines(self) -> list[str]:
        """Updates self._output and gets widget lines."""

        if self.show_output and self._matrix.selected_pixel is not None:
            _, color = self._matrix.selected_pixel
            if len(color) == 0:
                return super().get_lines()

            color_obj = str_to_color(color)
            rgb = color_obj.rgb
            hex_ = color_obj.hex
            lines: list[Widget] = [
                Label(f"[@{color} #auto] {color} [/ {color}] {color}"),
                Label(
                    f"[{color} bold]Here[/bold italic] is "
                    + "[/italic underline]some[/underline dim] example[/dim] text"
                ),
                Label(),
                Label(
                    f"RGB: [{';'.join(map(str, rgb))}]"
                    + f"rgb({rgb[0]:>3}, {rgb[1]:>3}, {rgb[2]:>3})"
                ),
                Label(f"HEX: [{hex_}]{hex_}"),
            ]
            self._output.set_widgets(lines + [Label(), self.chosen])

            return super().get_lines()

        return super().get_lines()

__init__(show_output=True, attrs)

Initializes a ColorPicker.

Attrs

show_output: Decides whether the output Container should be added. If not set, the widget will only display the PixelMatrix of colors.

Source code in pytermgui/widgets/color_picker.py
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
def __init__(self, show_output: bool = True, **attrs: Any) -> None:
    """Initializes a ColorPicker.

    Attrs:
        show_output: Decides whether the output Container should be
            added. If not set, the widget will only display the
            PixelMatrix of colors.
    """

    super().__init__(**attrs)
    self.show_output = show_output

    self._matrix = PixelMatrix.from_matrix(_get_xterm_matrix())

    self.width = 72
    self.box = boxes.EMPTY

    self._add_widget(self._matrix, run_get_lines=False)

    self.chosen = Joiner()
    self._output = Container(self.chosen, "", "", "")

    if self.show_output:
        self._add_widget(self._output)

get_lines()

Updates self._output and gets widget lines.

Source code in pytermgui/widgets/color_picker.py
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
def get_lines(self) -> list[str]:
    """Updates self._output and gets widget lines."""

    if self.show_output and self._matrix.selected_pixel is not None:
        _, color = self._matrix.selected_pixel
        if len(color) == 0:
            return super().get_lines()

        color_obj = str_to_color(color)
        rgb = color_obj.rgb
        hex_ = color_obj.hex
        lines: list[Widget] = [
            Label(f"[@{color} #auto] {color} [/ {color}] {color}"),
            Label(
                f"[{color} bold]Here[/bold italic] is "
                + "[/italic underline]some[/underline dim] example[/dim] text"
            ),
            Label(),
            Label(
                f"RGB: [{';'.join(map(str, rgb))}]"
                + f"rgb({rgb[0]:>3}, {rgb[1]:>3}, {rgb[2]:>3})"
            ),
            Label(f"HEX: [{hex_}]{hex_}"),
        ]
        self._output.set_widgets(lines + [Label(), self.chosen])

        return super().get_lines()

    return super().get_lines()

handle_mouse(event)

Handles mouse events.

On hover, the widget will display the currently hovered color and some testing text.

On click, it will add a _FadeInButton for the currently hovered color.

Parameters:

Name Type Description Default
event MouseEvent

The event to handle.

required
Source code in pytermgui/widgets/color_picker.py
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def handle_mouse(self, event: MouseEvent) -> bool:
    """Handles mouse events.

    On hover, the widget will display the currently hovered
    color and some testing text.

    On click, it will add a _FadeInButton for the currently
    hovered color.

    Args:
        event: The event to handle.
    """

    if super().handle_mouse(event):
        return True

    if not self.show_output or not self._matrix.contains(event.position):
        return False

    if event.action is MouseAction.LEFT_CLICK:
        if self._matrix.selected_pixel is None:
            return True

        _, color = self._matrix.selected_pixel
        if len(color) == 0:
            return False

        button = _FadeInButton(f"{color:^5}", width=5)
        button.styles.label = f"@{color}"
        self.chosen.lazy_add(button)

        return True

    return False

selectables_length() property

Returns either the button count or 1.

Source code in pytermgui/widgets/color_picker.py
208
209
210
211
212
@property
def selectables_length(self) -> int:
    """Returns either the button count or 1."""

    return max(super().selectables_length, 1)

Joiner

Bases: Container

A Container that stacks widgets horizontally, without filling up the available space.

This works slightly differently to Splitter, as that applies padding & custom widths to any Widget it finds. This works much more simply, and only joins their lines together as they come.

Source code in pytermgui/widgets/color_picker.py
 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
class Joiner(Container):
    """A Container that stacks widgets horizontally, without filling up the available space.

    This works slightly differently to Splitter, as that applies padding & custom widths to
    any Widget it finds. This works much more simply, and only joins their lines together as
    they come.
    """

    parent_align = HorizontalAlignment.LEFT

    chars = {"separator": " "}

    def get_lines(self) -> list[str]:
        """Does magic"""

        lines: list[str] = []
        separator = self._get_char("separator")
        assert isinstance(separator, str)

        line = ""
        for widget in self._widgets:
            if len(line) > 0:
                line += separator

            widget.pos = (self.pos[0] + real_length(line), self.pos[1] + len(lines))
            widget_line = widget.get_lines()[0]

            if real_length(line + widget_line) >= self.width:
                lines.append(line)
                widget.pos = self.pos[0], self.pos[1] + len(lines)
                line = widget_line
                continue

            line += widget_line

        lines.append(line)
        self.height = len(lines)
        return lines

get_lines()

Does magic

Source code in pytermgui/widgets/color_picker.py
 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
def get_lines(self) -> list[str]:
    """Does magic"""

    lines: list[str] = []
    separator = self._get_char("separator")
    assert isinstance(separator, str)

    line = ""
    for widget in self._widgets:
        if len(line) > 0:
            line += separator

        widget.pos = (self.pos[0] + real_length(line), self.pos[1] + len(lines))
        widget_line = widget.get_lines()[0]

        if real_length(line + widget_line) >= self.width:
            lines.append(line)
            widget.pos = self.pos[0], self.pos[1] + len(lines)
            line = widget_line
            continue

        line += widget_line

    lines.append(line)
    self.height = len(lines)
    return lines