Skip to content

Commit 4a0f0d9

Browse files
authored
Merge pull request #5803 from TomJGooding/feat-color-add-hsv-support-to-color-class
feat(color): add HSV support to Color class
2 parents 339e933 + ff4971c commit 4a0f0d9

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1818

1919
- Exposed `CollapsibleTitle` https://github.com/Textualize/textual/pull/5810
2020

21+
### Added
22+
23+
- Added `Color.hsv` property and `Color.from_hsv` class method https://github.com/Textualize/textual/pull/5803
24+
2125
## [3.2.0] - 2025-05-02
2226

2327
### Fixed

src/textual/color.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from __future__ import annotations
3232

3333
import re
34-
from colorsys import hls_to_rgb, rgb_to_hls
34+
from colorsys import hls_to_rgb, hsv_to_rgb, rgb_to_hls, rgb_to_hsv
3535
from functools import lru_cache
3636
from operator import itemgetter
3737
from typing import Callable, NamedTuple
@@ -82,7 +82,7 @@ class HSV(NamedTuple):
8282
s: float
8383
"""Saturation in range 0 to 1."""
8484
v: float
85-
"""Value un range 0 to 1."""
85+
"""Value in range 0 to 1."""
8686

8787

8888
class Lab(NamedTuple):
@@ -212,6 +212,21 @@ def from_hsl(cls, h: float, s: float, l: float) -> Color:
212212
r, g, b = hls_to_rgb(h, l, s)
213213
return cls(int(r * 255 + 0.5), int(g * 255 + 0.5), int(b * 255 + 0.5))
214214

215+
@classmethod
216+
def from_hsv(cls, h: float, s: float, v: float) -> Color:
217+
"""Create a color from HSV components.
218+
219+
Args:
220+
h: Hue.
221+
s: Saturation.
222+
v: Value.
223+
224+
Returns:
225+
A new color.
226+
"""
227+
r, g, b = hsv_to_rgb(h, s, v)
228+
return cls(int(r * 255 + 0.5), int(g * 255 + 0.5), int(b * 255 + 0.5))
229+
215230
@property
216231
def inverse(self) -> Color:
217232
"""The inverse of this color.
@@ -286,6 +301,19 @@ def hsl(self) -> HSL:
286301
h, l, s = rgb_to_hls(r, g, b)
287302
return HSL(h, s, l)
288303

304+
@property
305+
def hsv(self) -> HSV:
306+
"""This color in HSV format.
307+
308+
HSV color is an alternative way of representing a color, which can be used in certain color calculations.
309+
310+
Returns:
311+
Color encoded in HSV format.
312+
"""
313+
r, g, b = self.normalized
314+
h, s, v = rgb_to_hsv(r, g, b)
315+
return HSV(h, s, v)
316+
289317
@property
290318
def brightness(self) -> float:
291319
"""The human perceptual brightness.

tests/test_color.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ def test_hsl():
5252
assert red.hsl.css == "hsl(356,81.8%,43.1%)"
5353

5454

55+
def test_hsv():
56+
red = Color(200, 20, 32)
57+
print(red.hsv)
58+
assert red.hsv == pytest.approx(
59+
(0.9888888888888889, 0.8999999999999999, 0.7843137254901961)
60+
)
61+
assert Color.from_hsv(
62+
0.9888888888888889, 0.8999999999999999, 0.7843137254901961
63+
).normalized == pytest.approx(red.normalized, rel=1e-5)
64+
65+
5566
def test_color_brightness():
5667
assert Color(255, 255, 255).brightness == 1
5768
assert Color(0, 0, 0).brightness == 0

0 commit comments

Comments
 (0)