1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 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
| # -*- coding:utf-8 -*-
class Couleur(object):
'''
Classe définissant des instances permettant d'avoir les équivalences
entre les valeurs rvb4 (4bits), rvb8 (8bits), tsl, hexa4, hexa8.
Ce programme est adapté de celui exposé $5.1 page 198 du livre "Python par
l'exemple" de A. Martelli, A.M. Ravenscroft & D. Ascher, collection
O'Reilly.
Le principe est que l'attribut central est rvb. A chaque demande ou création
d'une autre propriété, celle ci est calculée à la volée.
Pour se faire les méthodes internes __getattr__ et __setattr__ sont
redéfinies.
L'explosion combinatoire des fonctions nécessaires pour convertir
une propriété en une autre est ainsi évitée.
Les données d'entrées sont vérifiées:
0<=r,v,b entiers<=255 ou 65535
0<=t entier<=360
0.<=s flottant<=1.
0.<=l flottant<=1.
hexa string commençant par # et dont les
caractères autorisés sont 0à9 et a,b,c,d,e,f
>>> c=Couleur(rvb4=(56,110,89))
>>> c
(56,110,89)
>>> print(c)
rvb=(56, 110, 89)
>>> c.tsl
(157, 0.3253, 0.32549)
>>> c.hexa4
'#386e6e'
>>> c.hexa8
'#38386e6e5959'
>>> c.rvb8
(14392, 28270, 22873)
>>> c.rvb4
(56, 110, 89)
>>> c.tsl=(67,.89,.56)
>>> c.rvb4
(219, 243, 43)
>>> c.rvb8
(56375, 62363, 11036)
>>> c.tsl
(67, 0.89, 0.56)
'''
tupleRVB=('Rouge','Vert','Bleu')
tupleTSL=('Teinte','Saturation','Luminosité')
def rvbrvb4(rvb):
'''
Retourne les valeurs de rvb sous forme d'entier
'''
return round(rvb[0]),round(rvb[1]),round(rvb[2])
def rvb4rvb(rvb):
return (rvb[0],rvb[1],rvb[2])
def verifRvb4(rvb):
"""
Vérifie que l'attibut d'instance self._rvb donné possède bien les bons
types de variables rouge, vert et bleu qui sont des int compris entre
0 et 255.
"""
for i in range(0,3):
# if type(rvb[i]) is not int:
# raise TypeError("Valeur {0} doit être un entier pour la couleur {1}".format(rvb[i],Couleur.tupleRVB[i]))
if rvb[i]<0 or rvb[i]>255:
raise ValueError('Valeur {0} incorrecte pour la couleur {1}'.format(rvb[i],Couleur.tupleRVB[i]))
def rvbrvb8(rvb):
"""
Retourne les valeurs rvb4 d'une couleur à partir de ses valeurs rvb8.
0<=r4,v4,b4<=255
0<=r8,v8,b8<=65535
"""
return round(rvb[0]*257),round(rvb[1]*257),round(rvb[2]*257)
def rvb8rvb(rvb):
"""
Retourne les valeurs rvb8 d'une couleur à partir de ses valeurs rvb4.
0<=r4,v4,b4<=255
0<=r8,v8,b8<=65535
"""
return rvb[0]/257,rvb[1]/257,rvb[2]/257
def verifRvb8(rvb):
"""
Vérifie que l'attibut d'instance self._rvb donné possède bien les bons
types de variables rouge, vert et bleu qui sont des int compris entre
0 et 65535.
"""
for i in range(0,3):
if type(rvb[i]) is not int:
raise TypeError("Valeur {0} doit être un entier pour la couleur {1}".format(rvb[i],Couleur.tupleRVB[i]))
if rvb[i]<0 or rvb[i]>65535:
raise ValueError('Valeur {0} incorrecte pour la couleur {1}'.format(rvb[i],Couleur.tupleRVB[i]))
def rvbhexa4(rvb):
"""
Retourne la valeur héxadécimales (hexa) d'une couleur
à partir de ses valeurs rvb.
0<=r,v,b<=255
"""
r,v,b=round(rvb[0]),round(rvb[1]),round(rvb[1])
return f"#{r:0>2x}{v:0>2x}{b:0>2x}"
def hexa4rvb(val):
"""
Retourne les valeurs rvb (rouge, vert, bleu) d'une couleur
à partir de sa valeur héxadécimale (hexa).
0<=r,v,b<=255
"""
val = val.lstrip('#')
lv = len(val)
return tuple(int(val[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
def verifHexa4(val):
"""
Vérifie que valeur est bien une valeur de couleur en hexadécimal
"""
if val[0]!='#':
raise ValueError('Premier caractère {0} incorrect: doit être "#"'.format(val[0]))
if len(val)!=7:
raise ValueError('len({0})!=7'.format(valeur))
for i in range(1,7):
if val[i] not in '0123456789abcdef':
raise ValueError("{0} n'est pas une valeur de couleur correcte en hexadécimale".format(val))
def rvbhexa8(rvb):
"""
Retourne la valeur héxadécimales (hexa) d'une couleur
à partir de ses valeurs rvb8.
0<=r,v,b<=65535
"""
r,v,b=Couleur.rvbrvb8(rvb)
return f"#{r:0>4x}{v:0>4x}{b:0>4x}"
def hexa8rvb(val):
"""
Retourne les valeurs rvb4 (rouge, vert, bleu) d'une couleur
à partir de sa valeur héxadécimale sur 8 bits (hexa8).
0<=r,v,b<=255
"""
val = val.lstrip('#')
lv = len(val)
c=tuple(int(val[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
return (c[0]/257,c[1]/257,c[2]/257)
def verifHexa8(val):
"""
Vérifie que valeur est bien une valeur de couleur en hexadécimal
"""
if val[0]!='#':
raise ValueError('Premier caractère {0} incorrect: doit être "#"'.format(val[0]))
if len(val)!=13:
raise ValueError('len({0})!=13'.format(valeur))
for i in range(1,13):
if val[i] not in '0123456789abcdef':
raise ValueError("{0} n'est pas une valeur de couleur correcte en hexadécimale".format(val))
def rvbtsl(rvb):
"""
Retourne les valeurs tsl (teinte, saturation, luminance) d'une couleur
à partir de ses valeurs RVB.
0<=r,v,b<=255
0<=t<=360 0.<=s<=1. 0.<=l<=1.
"""
R,V,B=rvb[0],rvb[1],rvb[2]
M=max(R,V,B)
m=min(R,V,B)
chroma=M-m
if chroma==0:
t=0
elif M==R:
t=(((V-B)/chroma)%6)*60
elif M==V:
t=((2+(B-R)/chroma)%6)*60
elif M==B:
t=((4+(R-V)/chroma)%6)*60
L=(m+M)/2/255
if L==0: s=0.
elif L==1: s=0.
else: s=chroma/(1-abs(2*L-1))/255
return(round(t),round(s,5),round(L,5))
def tslrvb(tsl=(0,0,0)):
"""
Retourne les valeurs rvb (rouge, vert, bleu) d'une couleur
à partir de ses valeurs tsl.
0<=r,v,b<=255
0<=t<=360 0.<=s<=1. 0.<=l<=1.
"""
t,s,l=tsl[0],tsl[1],tsl[2]
chroma=(1-abs(2*l-1))*s
tp=t/60
x=chroma*(1-abs(tp%2-1))
if t==0:
r,v,b=(0,0,0)
elif 0<=tp and tp<1:
r,v,b=Chroma,x,0
elif 1<=tp and tp<2:
r,v,b=x,chroma,0
elif 2<=tp and tp<=3:
r,v,b=0,chroma,x
elif 3<=tp and tp<4:
r,v,b=0,x,chroma
elif 4<=tp and tp<5:
r,v,b=x,0,chroma
elif 5<=tp and tp<6:
r,v,b=chroma,0,x
m=l-chroma/2
r,v,b=(r+m)*255,(v+m)*255,(b+m)*255
return (r,v,b)
def verifTsl(val):
"""
Vérifie que le tupple val est constitué de 3 entier ou flottants
le premier doit être compris entre 0 et 360 (la teinte)
les 2 derniers compris entre 0 et 1 (la saturation et la luminance)
"""
for i in range(3):
if type(val[i]) is not int and type(val[i]) is not float:
raise TypeError("Valeur {0} doit être un entier ou un flottant pour la {1}".format(val[0],
Couleur.tupleTSL[0]))
if val[0]<0 or val[0]>=360:
raise ValueError('Valeur {0} incorrecte pour la {1}'.format(val[0],Couleur.tupleTSL[0]))
for i in range(1,3):
if val[i]<0 or val[i]>1:
raise ValueError('Valeur {0} incorrecte pour couleur {1}'.format(val[i],Couleur.tupleTSL[i]))
fonctions={'rvb4':(rvbrvb4,rvb4rvb,verifRvb4),
'rvb8':(rvbrvb8,rvb8rvb,verifRvb8),
'hexa4':(rvbhexa4,hexa4rvb,verifHexa4),
'hexa8':(rvbhexa8,hexa8rvb,verifHexa8),
'tsl':(rvbtsl,tslrvb,verifTsl)}
def __init__(self,**mots):
try:
nom,valeur=mots.popitem()
except KeyError:
nom,valeur='_rvb',(0,0,0)
if mots or nom not in ['_rvb','rvb4','rvb8','hexa4','hexa8','tsl','pantome','tkinter','ral','francais']:
mots[nom]=valeur
raise TypeError('Paramètres incorrects {0}'.format(mots))
setattr(self,nom,valeur)
def __getattr__(self,nom):
try:
return Couleur.fonctions[nom][0](self._rvb)
except KeyError:
raise AttributError(nom)
def __setattr__(self,nom,valeur):
if nom in ['rvb4','rvb8','hexa4','hexa8','tsl','pantome','tkinter','ral','francais']:
Couleur.fonctions[nom][2](valeur)
self._rvb=Couleur.fonctions[nom][1](valeur)
elif nom=='_rvb': # Pb d'appel récursif à __setattr__ si ce teste est absent
Couleur.verifRvb4(valeur)
object.__setattr__(self,nom,valeur)
else:
raise AttributError(nom)
def __str__(self):
c=round(self._rvb[0]),round(self._rvb[1]),round(self._rvb[2])
return f"rvb={(c[0],c[1],c[2])}"
def __repr__(self):
c=round(self._rvb[0]),round(self._rvb[1]),round(self._rvb[2])
return f"({c[0]},{c[1]},{c[2]})" |