Coverage for qubalab/objects/classification.py: 90%

49 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-10-22 18:11 +0000

1from __future__ import annotations 

2import random 

3from typing import Optional, Union 

4 

5 

6class Classification(object): 

7 """ 

8 Simple class to store the names and color of a classification. 

9 

10 Each Classification with the same names is the same object, retrieved from a cache. 

11 Therefore updating the color of a Classification will update all similarly classified objects. 

12 """ 

13 

14 _cached_classifications: dict[tuple[str], Classification] = {} 

15 

16 def __new__( 

17 cls, names: Union[str, tuple[str]], color: Optional[tuple[int, int, int]] = None 

18 ): 

19 if isinstance(names, str): 

20 names = (names,) 

21 elif isinstance(names, list): 

22 names = tuple(names) 

23 if names is None: 

24 return None 

25 if not isinstance(names, tuple): 

26 raise TypeError("names should be str or tuple[str]") 

27 

28 classification = Classification._cached_classifications.get(names) 

29 if classification is None: 

30 classification = super().__new__(cls) 

31 Classification._cached_classifications[names] = classification 

32 

33 if color is not None: 

34 classification.color = color 

35 return classification 

36 

37 def __init__( 

38 self, 

39 names: Union[str, tuple[str]], 

40 color: Optional[tuple[int, int, int]] = None, 

41 ): 

42 """ 

43 :param names: the names of the classification 

44 :param color: the RGB color (each component between 0 and 255) of the classification. Can be None to use a random color 

45 """ 

46 if isinstance(names, str): 

47 names = (names,) 

48 elif isinstance(names, list): 

49 names = tuple(names) 

50 if not isinstance(names, tuple): 

51 raise TypeError("names should be a tuple, list or string") 

52 self._names = names 

53 self._color: tuple = ( 

54 tuple(random.randint(0, 255) for _ in range(3)) if color is None else color 

55 ) 

56 

57 @property 

58 def names(self) -> tuple[str]: 

59 """ 

60 The names of the classification. 

61 """ 

62 return self._names 

63 

64 @property 

65 def color(self) -> tuple[int, int, int]: 

66 """ 

67 The color of the classification. 

68 """ 

69 return self._color ## todo: pylance type hints problem 

70 

71 @color.setter 

72 def color(self, value: tuple[int, int, int]) -> None: 

73 """ 

74 Change the color of the classification. 

75 :param value: the new 8-bit RGB color 

76 """ 

77 self._color = value 

78 

79 def __str__(self): 

80 return f"Classification {self.names} of color {self.color}" 

81 

82 def __repr__(self): 

83 return f"Classification('{self.names}', {self.color})" 

84 

85 def __eq__(self, other): 

86 if isinstance(other, Classification): 

87 return (self is other) or (self.names == other.names) 

88 return False 

89 

90 def __hash__(self): 

91 return hash(self.names)