14
14
# Union, TraitError, HasTraits,
15
15
# NoDefaultSpecified, TraitType)
16
16
import numpy as np
17
+ import re
17
18
18
19
# override for backward compatability
19
20
class Configurable (Configurable ): pass
@@ -60,31 +61,30 @@ class Color(TraitType):
60
61
Arguments:
61
62
force_rgb: bool: Force the return in RGB format instead of RGB. Default: False
62
63
as_hex: bool: Return the hex value instead. Default: False
63
- default_alpha: float (0.0-1.0) or integer (0-255) default alpha value.
64
+ default_alpha: float (0.0-1.0) or integer (0-255). Default alpha value (1.0)
64
65
65
66
Accepts:
66
- string: a valid hex color string (i.e. #FFFFFF). 7 chars
67
+ string: a valid hex color string (i.e. #FFFFFF). With 4 or 7 chars.
67
68
tuple: a tuple of ints (0-255), or tuple of floats (0.0-1.0)
68
69
float: A gray shade (0-1)
69
70
integer: A gray shade (0-255)
70
71
71
- Defaults: RGBA tuple, color black (0.0, 0.0, 0.0, 0 .0)
72
+ Defaults: RGBA tuple, color black (0.0, 0.0, 0.0, 1 .0)
72
73
73
74
Return:
74
- A hex color string, a rgb or a rgba tuple. Defaults to rgba. When
75
- returning hex string, the alpha property will be ignored. A warning
76
- will be emitted if alpha information is passed different then 0.0
75
+ A tuple of floats (r,g,b,a), (r,g,b) or a hex color string. i.e. "#FFFFFF".
77
76
78
77
"""
79
78
metadata = {
80
79
'force_rgb' : False ,
81
80
'as_hex' : False ,
82
- 'default_alpha' : 0 .0 ,
81
+ 'default_alpha' : 1 .0 ,
83
82
}
84
83
allow_none = True
85
84
info_text = 'float, int, tuple of float or int, or a hex string color'
86
- default_value = (0.0 ,0.0 ,0.0 ,0.0 )
85
+ default_value = (0.0 ,0.0 ,0.0 , metadata [ 'default_alpha' ] )
87
86
named_colors = {}
87
+ _re_color_hex = re .compile (r'#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$' )
88
88
89
89
def _int_to_float (self , value ):
90
90
as_float = (np .array (value )/ 255 ).tolist ()
@@ -100,18 +100,13 @@ def _int_to_hex(self, value):
100
100
return as_hex
101
101
102
102
def _hex_to_float (self , value ):
103
- # Expects #FFFFFF format
104
- split_hex = (value [1 :3 ],value [3 :5 ],value [5 :7 ])
105
- as_float = (np .array ([int (v ,16 ) for v in split_hex ])/ 255.0 ).tolist ()
103
+ if len (value ) == 7 :
104
+ split_hex = (value [1 :3 ],value [3 :5 ],value [5 :7 ])
105
+ as_float = (np .array ([int (v ,16 ) for v in split_hex ])/ 255.0 ).tolist ()
106
+ elif len (value ) == 4 :
107
+ as_float = (np .array ([int (v + v ,16 ) for v in value [1 :]])/ 255.0 ).tolist ()
106
108
return as_float
107
109
108
- def _is_hex16 (self , value ):
109
- try :
110
- int (value , 16 )
111
- return True
112
- except :
113
- return False
114
-
115
110
def _float_to_shade (self , value ):
116
111
grade = value * 255.0
117
112
return (grade ,grade ,grade )
@@ -122,21 +117,28 @@ def _int_to_shade(self, value):
122
117
123
118
def validate (self , obj , value ):
124
119
in_range = False
125
- if value is None or value is False or value in ['none' ,'' ]:
126
- # Return transparent if no other default alpha was set
127
- return (0.0 , 0.0 , 0.0 , 1.0 )
120
+ if value is True :
121
+ self .error (obj , value )
128
122
129
- if isinstance (value , float ) and 0 <= value <= 1 :
130
- value = self ._float_to_shade (value )
131
- else :
132
- in_range = False
123
+ elif value is None or value is False or value in ['none' ,'' ]:
124
+ value = (0.0 , 0.0 , 0.0 , 0.0 )
125
+ in_range = True
133
126
134
- if isinstance (value , int ) and 0 <= value <= 255 :
135
- value = self ._int_to_shade (value )
136
- else :
137
- in_range = False
127
+ elif isinstance (value , float ):
128
+ if 0 <= value <= 1 :
129
+ value = self ._float_to_shade (value )
130
+ in_range = True
131
+ else :
132
+ in_range = False
138
133
139
- if isinstance (value , (tuple , list )) and len (value ) in (3 ,4 ):
134
+ elif isinstance (value , int ):
135
+ if 0 <= value <= 255 :
136
+ value = self ._int_to_shade (value )
137
+ in_range = True
138
+ else :
139
+ in_range = False
140
+
141
+ elif isinstance (value , (tuple , list )) and len (value ) in (3 ,4 ):
140
142
is_all_float = np .prod ([isinstance (v , (float )) for v in value ])
141
143
in_range = np .prod ([(0 <= v <= 1 ) for v in value ])
142
144
if is_all_float and in_range :
@@ -147,10 +149,8 @@ def validate(self, obj, value):
147
149
if is_all_int and in_range :
148
150
value = self ._int_to_float (value )
149
151
150
- if isinstance (value , str ) and len (value ) == 7 and value [0 ] == '#' :
151
- is_all_hex16 = np .prod ([self ._is_hex16 (v ) for v in \
152
- (value [1 :3 ],value [3 :5 ],value [5 :7 ])])
153
- if is_all_hex16 :
152
+ elif isinstance (value , str ) and len (value ) in [4 ,7 ] and value [0 ] == '#' :
153
+ if self ._re_color_hex .match (value ):
154
154
value = self ._hex_to_float (value )
155
155
in_range = np .prod ([(0 <= v <= 1 ) for v in value ])
156
156
if in_range :
@@ -159,18 +159,24 @@ def validate(self, obj, value):
159
159
elif isinstance (value , str ) and value in self .named_colors :
160
160
value = self .validate (obj , self .named_colors [value ])
161
161
in_range = True
162
-
162
+
163
163
if in_range :
164
+ # Convert to hex color string
164
165
if self ._metadata ['as_hex' ]:
165
166
return self ._float_to_hex (value )
167
+
168
+ # Ignores alpha and return rgb
166
169
if self ._metadata ['force_rgb' ] and in_range :
167
170
return tuple (np .round (value [:3 ],5 ).tolist ())
168
- else :
169
- if len (value ) == 3 :
170
- value = tuple (np .round ((value [0 ], value [1 ], value [2 ],
171
- self ._metadata ['default_alpha' ]),5 ).tolist ())
172
- elif len (value ) == 4 :
173
- value = tuple (np .round (value ,5 ).tolist ())
171
+
172
+ # If no alpha provided, use default_alpha, also round the output
173
+ if len (value ) == 3 :
174
+ value = tuple (np .round ((value [0 ], value [1 ], value [2 ],
175
+ self ._metadata ['default_alpha' ]),5 ).tolist ())
176
+ elif len (value ) == 4 :
177
+ # If no alpha provided, use default_alpha
178
+ value = tuple (np .round (value ,5 ).tolist ())
179
+
174
180
return value
175
181
176
182
self .error (obj , value )
0 commit comments