libs.zazac.link

simplesuit.colors v0.2.1

Couleurs RGB 24 bits, dégradés et styles pour le terminal

Inclus dans simplesuit : installe simplesuit pour l'avoir.

import simplesuit.colors · PyPI : simplesuit · Python 3.9+ · dépend de psutil

Guide

Des couleurs RGB 24 bits dans le terminal : 16 millions de couleurs, qui s'affichent pareil quel que soit le thème de ton terminal. Par nom ("tomato"), en HEX ("#FF6347") ou en RGB ((255, 99, 71)), avec un fond, du gras, de l'italique, et des dégradés.

Colorer un texte#

python
from simplesuit import colors as sc

print(sc.color("Salut", "tomato"))                                  # Couleur du texte
print(sc.color("Alerte", "white", bg="crimson", styles="bold"))     # + fond + gras
print(sc.color("Note", "#89B4FA", styles=["italic", "underline"]))  # Plusieurs styles
print(sc.color("RGB", (120, 200, 255)))                             # En RGB

color() renvoie un texte (il ne l'affiche pas) : tu peux le mettre dans un print, une f-string, ou le coller à d'autres morceaux.

python
print(f"{sc.green('OK')} · 3 fichiers copiés · {sc.red('1 erreur')}")

Les raccourcis#

Chaque nom de la palette devient une fonction : sc.red("…") au lieu de sc.color("…", "red"). Les options marchent aussi :

python
print(sc.mint("succès"), sc.peach("attention"), sc.mochablue("info"))
print(sc.tomato("Gros titre", styles="bold", bg="black"))
print(sc.bold("gras"), sc.italic("italique"), sc.underline("souligné"))

Les thèmes de terminal connus sont dans la palette

Catppuccin Mocha (mochablue, mochapeach, mochagreen…), Dracula (draculapink…) et Nord (nordblue…) : tes couleurs vont avec ton thème.

Les formats acceptés#

Partout où une couleur est demandée (color, bg, gradient, to_rgb…) :

FormatExemple
un nom de la palette"tomato", "Tomato", "dark_red" (majuscules et _ ignorés)
HEX à 6 ou 3 chiffres"#FF6347", "FF6347", "#F00"
texte RGB"rgb(255, 99, 71)"
tuple ou liste RGB(255, 99, 71)
python
sc.to_rgb("tomato")      # (255, 99, 71)
sc.to_rgb("#F00")        # (255, 0, 0)

Styles#

bold (gras), dim (pâle), italic, underline (souligné), blink (clignote), reverse (inverse texte et fond), strike (barré). Un seul en texte, plusieurs dans une liste : styles=["bold", "italic"].

Selon le terminal

Tous les terminaux ne savent pas tout faire : blink est souvent ignoré, et l'italique dépend de la police.

Dégradés#

Un dégradé lettre par lettre, entre autant de couleurs que tu veux (2 minimum) :

python
print(sc.gradient("Bienvenue dans orsh", "cyan", "magenta"))
print(sc.gradient("════════════════════════", "yellow", "orange", "red"))
print(sc.gradient("TITRE", "#89B4FA", "#F5C2E7", styles="bold"))

Une bannière en 3 lignes

Un dégradé sur une ligne de ═ au-dessus et en dessous d'un titre fait une très jolie bannière de démarrage. Voir l'exemple « Une bannière de démarrage ».

Trouver une couleur#

python
sc.show_color("tomato")       # Aperçu en texte et en fond, + nom, HEX, RGB
sc.show_palette()             # Toute la palette (246 couleurs)
résultat
  ████████
  ████████   Texte
  ████████    Fond
  name: tomato
  hex:  #FF6347
  rgb:  (255, 99, 71)

Une faute de frappe ? Le message propose les noms proches :

résultat
ValueError: simplesuit.colors : unknown color: tomatto (did you mean: tomato ?)
ValueError: simplesuit.colors : unknown color: mocha (did you mean: mochabase, mochacrust, mochasurface0, mochasurface1, mochasurface2... ?)

Astuces et pièges#

len() d'un texte coloré

Une couleur ajoute des codes invisibles (\033[38;2;…m) : len(sc.red("abc")) vaut bien plus que 3. Pour aligner un tableau, mesure len(sc.strip(texte)), ou formate le texte avant de le colorer : sc.red(f"{nom:<10}").

Les couleurs dans un fichier

python3 main.py > sortie.txt écrit aussi les codes de couleur dans le fichier. Pour un texte propre : sc.strip(texte) avant de l'écrire.

Des couleurs à toi

Garde tes couleurs dans des constantes (TITRE = "#89B4FA") et utilise sc.color(texte, TITRE) : changer de thème, c'est changer une ligne.

Les couleurs ne s'affichent pas, je vois des [38;2;… à la place

Ton terminal ne comprend pas les couleurs 24 bits (l'ancien Terminal de Windows, certains éditeurs). Le Terminal de macOS, iTerm2, VS Code, Windows Terminal et presque tous les terminaux Linux les comprennent.

Exemples

Couleurs et styles

Colorer un texte par nom, HEX ou RGB, avec un fond et des styles (gras, italique…).

couleurs_et_styles.py
from simplesuit import colors as sc

print(sc.color("Par nom", "tomato"))
print(sc.color("En HEX", "#1E90FF"))
print(sc.color("En RGB", (120, 200, 80)))
print(sc.color(" Avec un fond ", "white", bg="crimson", styles="bold"))
print(sc.color("Plusieurs styles", "gold", styles=["bold", "underline"]))

# Raccourci : le nom de la couleur devient une fonction
print(sc.mint("succès"), sc.error("erreur"), sc.mochamauve("Catppuccin"))
print(sc.bold("gras"), sc.italic("italique"), sc.underline("souligné"))

Dégradés

Un dégradé lettre par lettre, avec autant de couleurs que tu veux.

degrades.py
from simplesuit import colors as sc

print(sc.gradient("Bienvenue dans orsh", "cyan", "magenta"))
print(sc.gradient("Coucher de soleil", "gold", "orange", "crimson", styles="bold"))
print(sc.gradient("━" * 40, "mochablue", "mochamauve", "mochapink"))

# strip() enlève les couleurs : pratique pour mesurer la vraie longueur
titre = sc.gradient("Titre", "lime", "teal")
print(len(titre), "caractères avec les codes,", len(sc.strip(titre)), "sans")

Trouver une couleur

Voir une couleur en détail, convertir en RGB, et se faire proposer un nom quand on se trompe.

trouver_une_couleur.py
from simplesuit import colors as sc

sc.show_color("coral")          # Aperçu + nom, HEX et RGB
print(sc.to_rgb("#F80"))        # (255, 136, 0)
print(sc.to_rgb("rgb(1, 2, 3)"))  # (1, 2, 3)

try:
    sc.color("texte", "tomatto")
except ValueError as e:
    print(e)  # simplesuit.colors : unknown color: tomatto (did you mean: tomato ?)

# sc.show_palette() affiche les 246 couleurs disponibles

Jauge de batterie en couleur

sys donne les chiffres, colors et icons les mettent en forme.

jauge_batterie.py
from simplesuit import colors as sc
from simplesuit import icons as si
from simplesuit import sys as ss

niveau = ss.battery()
if niveau is None:
    print("Pas de batterie sur cet ordinateur")
else:
    couleur = "lime" if niveau > 50 else "orange" if niveau > 20 else "red"
    barre = "█" * (niveau // 5) + "░" * (20 - niveau // 5)
    reste = ss.time_left_text() or "-"
    print(si.battery(niveau, ss.plugged()), sc.color(barre, couleur), f"{niveau} %  (reste {reste})")

Demander un mot de passe avec des *

password() affiche une * par lettre tapée. Retour arrière efface, Ctrl+U efface tout, Ctrl+C ou Ctrl+D annulent (KeyboardInterrupt / EOFError).

mot_de_passe.py
from simplesuit import colors as sc
from simplesuit import sys as ss

try:
    mdp = ss.password("password > ")
    confirmation = ss.password("encore une fois > ", mask="•")
except (KeyboardInterrupt, EOFError):
    exit("\nannulé")

if mdp != confirmation:
    exit(sc.red("Les deux mots de passe sont différents"))
print(sc.green(f"OK, {len(mdp)} caractères"))  # On n'affiche jamais le mot de passe lui-même

Une bannière de démarrage

Un dégradé sur des lignes de ═ autour d'un titre : 3 lignes pour un démarrage qui a de la gueule.

banniere.py
from simplesuit import colors as sc
from simplesuit import icons as si

LARGEUR = 40
titre = f" {si.rocket}  mon super programme  {si.rocket} "

print(sc.gradient("═" * LARGEUR, "cyan", "magenta"))
print(sc.gradient(titre.center(LARGEUR), "cyan", "magenta", styles="bold"))
print(sc.gradient("═" * LARGEUR, "magenta", "cyan"))
print(sc.color("v1.0 · tape help pour l'aide".center(LARGEUR), "gray", styles="italic"))

Mini tableau de bord

Toutes les secondes : on efface l'écran, puis on affiche les infos avec des icônes et des couleurs. Ctrl+C pour quitter.

tableau_de_bord.py
import time

from simplesuit import colors as sc
from simplesuit import icons as si
from simplesuit import sys as ss


# Une barre de progression colorée selon le niveau (vert, orange, rouge)
def barre(pourcentage, largeur=20):
    plein = round(pourcentage / 100 * largeur)
    couleur = "mint" if pourcentage < 60 else "orange" if pourcentage < 85 else "tomato"
    return sc.color("█" * plein, couleur) + sc.color("░" * (largeur - plein), "dimgray")


try:
    while True:
        cpu, ram, disque = ss.cpu(), ss.ram(), ss.disk()  # cpu() attend 0,3 s : c'est notre pause
        print("\033[2J\033[H", end="")                   # Effacer l'écran, curseur en haut
        print(sc.gradient(f" {ss.hostname()} ", "cyan", "magenta", styles="bold"), "\n")
        print(f"{si.cpu}  CPU    {barre(cpu)} {cpu:5.1f} %")
        print(f"{si.memory}  RAM    {barre(ram)} {ram:5.1f} %  {ss.ram_used()}")
        print(f"{si.disk}  Disque {barre(disque)} {disque:5.1f} %  {ss.disk_free()} libres")
        if ss.battery() is not None:
            print(f"{si.battery(ss.battery(), bool(ss.plugged()))}  Batterie {ss.battery()} %")
        print(sc.color(f"\nAllumé depuis {ss.uptime_text()} · Ctrl+C pour quitter", "gray"))
        time.sleep(0.7)
except KeyboardInterrupt:
    print()

Son propre ls, avec des icônes

file_icon() choisit l'icône selon le type : dossier, .py, .json, .md, image, musique… Les dossiers en bleu et en premier, les tailles alignées à droite.

mon_ls.py
import os

from simplesuit import colors as sc
from simplesuit import icons as si
from simplesuit import sys as ss

noms = sorted(os.listdir("."), key=lambda n: (not os.path.isdir(n), n.lower()))

for nom in noms:
    if nom.startswith("."):
        continue
    if os.path.isdir(nom):
        print(sc.color(f"{si.file_icon(nom)}  {nom}/", "cornflowerblue", styles="bold"))
    else:
        taille = ss.human_size(os.path.getsize(nom))
        print(f"{si.file_icon(nom)}  {nom:<30} {sc.color(f'{taille:>9}', 'gray')}")

Référence

Fonctions

to_rgb(color)

Transformer n'importe quelle couleur en (r, g, b)

Accepte : "red", "#FF0000", "#F00", "FF0000", "rgb(255, 0, 0)", (255, 0, 0)

Voir le code
python
def to_rgb(color):
    # Déjà un tuple / une liste (r, g, b)
    if isinstance(color, (tuple, list)):
        r, g, b = color
        return _check(r, g, b)

    text = str(color).strip().lower().replace(" ", "").replace("_", "")

    # Nom de la palette
    if text in PALETTE:
        text = PALETTE[text].lower()

    # "rgb(255,0,0)"
    match = re.fullmatch(r"rgb\((\d+),(\d+),(\d+)\)", text)
    if match:
        return _check(*(int(x) for x in match.groups()))

    # HEX : "#ff0000", "ff0000", "#f00"
    text = text.lstrip("#")
    if re.fullmatch(r"[0-9a-f]{3}", text):
        text = "".join(c * 2 for c in text)  # "f00" -> "ff0000"
    if re.fullmatch(r"[0-9a-f]{6}", text):
        return int(text[0:2], 16), int(text[2:4], 16), int(text[4:6], 16)

    raise ValueError(f"simplesuit.colors : unknown color: {color}" + _suggest(text))

color(text, color=None, bg=None, styles=None)

Colorer un texte

color = couleur du texte, bg = couleur du fond, styles = "bold", "italic"...

Excolor("Salut", "red", bg="black", styles=["bold"])

Voir le code
python
def color(text, color=None, bg=None, styles=None):
    codes = ""
    if color is not None:
        r, g, b = to_rgb(color)
        codes += f"\033[38;2;{r};{g};{b}m"
    if bg is not None:
        r, g, b = to_rgb(bg)
        codes += f"\033[48;2;{r};{g};{b}m"
    codes += _style_codes(styles)
    return f"{codes}{text}{RESET}"

bold(text)

Raccourcis pour les styles

Voir le code
python
def bold(text):
    return color(text, styles="bold")

italic(text)

Pas de description.

Voir le code
python
def italic(text):
    return color(text, styles="italic")

underline(text)

Pas de description.

Voir le code
python
def underline(text):
    return color(text, styles="underline")

gradient(text, *colors, styles=None)

Dégradé lettre par lettre, avec autant de couleurs que tu veux (2 minimum)

Exgradient("orsh", "cyan", "magenta")

gradient("orsh", "yellow", "orange", "red")

gradient("orsh", "cyan", "magenta", styles="bold")

Voir le code
python
def gradient(text, *colors, styles=None):
    if len(colors) < 2:
        raise ValueError("simplesuit.colors : gradient needs at least 2 colors")
    rgbs = [to_rgb(c) for c in colors]
    steps = max(len(text) - 1, 1)
    result = _style_codes(styles)  # Les styles restent actifs jusqu'au RESET final
    for i, char in enumerate(text):
        # Position dans le texte (0 = début, 1 = fin), étalée sur les morceaux du dégradé
        pos = i / steps * (len(rgbs) - 1)
        part = min(int(pos), len(rgbs) - 2)  # Entre quelles couleurs on est
        t = pos - part                        # Avancement entre ces 2 couleurs (0 -> 1)
        (r1, g1, b1), (r2, g2, b2) = rgbs[part], rgbs[part + 1]
        r = round(r1 + (r2 - r1) * t)
        g = round(g1 + (g2 - g1) * t)
        b = round(b1 + (b2 - b1) * t)
        result += f"\033[38;2;{r};{g};{b}m{char}"
    return result + RESET

strip(text)

Enlever les couleurs d'un texte (pratique pour mesurer sa vraie longueur)

Voir le code
python
def strip(text):
    return re.sub(r"\033\[[0-9;]*m", "", text)

show_palette()

Afficher toute la palette pour choisir une couleur

Voir le code
python
def show_palette():
    for name, hex_code in PALETTE.items():
        print(color("  ██  ", name), hex_code, name)

show_color(c)

Afficher une seule couleur en détail (accepte tous les formats : nom, HEX, RGB)

Exshow_color("tomato") / show_color("#FF8800") / show_color((120, 200, 255))

Voir le code
python
def show_color(c):
    r, g, b = to_rgb(c)
    hex_code = f"#{r:02X}{g:02X}{b:02X}"
    # Tous les noms de la palette qui ont cette couleur (ex : "aqua" et "cyan")
    names = [name for name, value in PALETTE.items() if value.upper() == hex_code]

    print(color("  ████████  ", (r, g, b)))                                              # Aperçu
    print(color("  ████████  ", (r, g, b)), color("Texte", (r, g, b), styles="bold"))    # En texte
    print(color("  ████████  ", (r, g, b)), color(" Fond ", bg=(r, g, b)))                # En fond
    print(f"  name: {', '.join(names) if names else '-'}")
    print(f"  hex:  {hex_code}")
    print(f"  rgb:  ({r}, {g}, {b})")

Constantes

RESET

Codes de style (gras, italique...) : ceux-là ne dépendent pas du thème du terminal

RESET = "\033[0m"

STYLES

bold'\x1b[1m'
dim'\x1b[2m'
italic'\x1b[3m'
underline'\x1b[4m'
blink'\x1b[5m'
reverse'\x1b[7m'
strike'\x1b[9m'

PALETTE

Toutes les couleurs sont en HEX (couleurs RGB "vraies", 24 bits)

-> elles s'affichent pareil quel que soit le thème du terminal

Ce sont les 148 couleurs nommées du web (CSS), + quelques extras à la fin

246 couleurs · clique pour copier un nom

Raccourcis dynamiques

Magie : permet d'écrire sc.red("texte") au lieu de sc.color("texte", "red")

Python appelle cette fonction quand on demande un nom qui n'existe pas dans le module

Voir le code
python
def __getattr__(name):
    if name.lower() in PALETTE:
        return lambda text, **options: color(text, name, **options)
    raise AttributeError(f"module 'simplesuit.colors' has no attribute '{name}'" + _suggest(name.lower()))