
Fonctionnalités : * Gère les animations, "kikoo" ou non ; * Propose une fonction d'affichage stylisé (couleur, gras) plus complète ; * Propose une fonction récupérant la taille du terminal adaptée à pas mal d'environnements ; * Quelques bricoles pour limiter la taille d'un texte et formatter les pourcentages, tout en choisissant une couleur en fonction de ceux-ci. * Propose une fonction prettyDoin qui permet de faire de l'affichage service-like pour des opérations en cours. Je le laisse dans gestion/ pour que les gens voient qu'il concurrence affich_tools.
261 lines
8.1 KiB
Python
Executable file
261 lines
8.1 KiB
Python
Executable file
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import sys
|
|
import os
|
|
import fcntl
|
|
import termios
|
|
import struct
|
|
import functools
|
|
|
|
def static_var(name, val):
|
|
"""Decorator setting static variable
|
|
to a function.
|
|
|
|
"""
|
|
def decorate(fun):
|
|
functools.wraps(fun)
|
|
setattr(fun, name, val)
|
|
return fun
|
|
return decorate
|
|
|
|
def getTerminalSize():
|
|
"""Dummy function to get term dimensions.
|
|
Thanks to http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python
|
|
|
|
Could be done using only env variables or thing like stty size, but would be less
|
|
portable.
|
|
|
|
"""
|
|
env = os.environ
|
|
def ioctl_GWINSZ(fd):
|
|
try:
|
|
cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
|
|
except:
|
|
return
|
|
return cr
|
|
cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
|
|
if not cr:
|
|
try:
|
|
fd = os.open(os.ctermid(), os.O_RDONLY)
|
|
cr = ioctl_GWINSZ(fd)
|
|
os.close(fd)
|
|
except:
|
|
pass
|
|
if not cr:
|
|
cr = (env.get('LINES', 25), env.get('COLUMNS', 80))
|
|
|
|
### Use get(key[, default]) instead of a try/catch
|
|
#try:
|
|
# cr = (env['LINES'], env['COLUMNS'])
|
|
#except:
|
|
# cr = (25, 80)
|
|
return int(cr[1]), int(cr[0])
|
|
|
|
def cap_text(text, length):
|
|
"""Cap text length to length
|
|
|
|
"""
|
|
if len(text) > length:
|
|
return text[0:length-1] + "…"
|
|
else:
|
|
return text
|
|
|
|
def format_percent(percent):
|
|
"""Formatte les pourcentages en ajoutant des espaces si
|
|
nécessaire.
|
|
|
|
"""
|
|
|
|
if percent < 10:
|
|
return " %s" % (percent,)
|
|
elif percent < 100:
|
|
return " %s" % (percent,)
|
|
else:
|
|
return str(percent)
|
|
|
|
def rojv(percent, seuils=(100, 75, 50, 25), couls=("cyan", "vert", "jaune", "orange", "rouge")):
|
|
"""Retourne la couleur qui va bien avec le pourcentage
|
|
|
|
Les pourcentages seuil, et les couleurs associées, doivent être
|
|
rangés en ordre décroissant.
|
|
|
|
"""
|
|
lens = len(seuils)
|
|
lenc = len(couls)
|
|
if lens+1 == lenc:
|
|
coul = couls[0]
|
|
for i in range(lens):
|
|
if percent < seuils[i]:
|
|
coul = couls[i+1]
|
|
else:
|
|
raise EnvironmentError("Seuils doit contenir une variable de moins par rapport à couls")
|
|
return coul
|
|
|
|
class Animation(object):
|
|
"""Propose une animation lors de la mise à jour ou de
|
|
l'évolution d'une tâche.
|
|
|
|
Si le nombre de cycles est défini, affiche un pourcentage
|
|
d'évolution.
|
|
|
|
Si l'option kikoo est définit, met une jauge en plus.
|
|
|
|
Sinon, affiche un truc qui tourne.
|
|
|
|
"""
|
|
|
|
def __init__(self, texte, nb_cycles=0, couleur=True, kikoo=True):
|
|
"""__init__
|
|
|
|
"""
|
|
self.texte = texte
|
|
self.nb_cycles = nb_cycles
|
|
self.kikoo = kikoo
|
|
self.step = 0
|
|
self.couleur = couleur
|
|
|
|
def kikoo(self, kikoo):
|
|
"""Switch pour la valeur kikoo"""
|
|
self.kikoo = kikoo
|
|
|
|
def new_step(self):
|
|
"""Effectue une étape dans l'affichage
|
|
|
|
"""
|
|
cols, _ = getTerminalSize()
|
|
|
|
if not self.nb_cycles > 0:
|
|
sys.stdout.write("\r%s ..... %s" % (cap_text(self.texte, cols - 10), "\|/-"[self.step%4]))
|
|
else:
|
|
percent = int((self.step+1.0)/self.nb_cycles*100)
|
|
if not self.kikoo or cols <= 55:
|
|
if self.couleur:
|
|
percent = style("%s %%" % (format_percent(percent),), rojv(percent))
|
|
else:
|
|
percent = "%s %%" % (format_percent(percent),)
|
|
sys.stdout.write("\r%s : %s" % (cap_text(self.texte, cols - 10), percent))
|
|
else:
|
|
# La kikoo bar est une barre de la forme [###### ]
|
|
# Nombre de #
|
|
amount = percent/4
|
|
# On remplit le reste avec des espaces.
|
|
if self.couleur:
|
|
# kikoo_print contient la barre et le pourcentage, colorés en fonction du pourcentage
|
|
kikoo_print = style("[%s%s%s] %s %%" % (amount * '=',
|
|
">",
|
|
(25-amount) * ' ',
|
|
format_percent(percent)),
|
|
rojv(percent))
|
|
|
|
else:
|
|
kikoo_print = "[%s%s%s] %s %%" % (amount * '=',
|
|
">",
|
|
(25-amount) * ' ',
|
|
format_percent(percent))
|
|
sys.stdout.write("\r%s %s" % (cap_text(self.texte, cols - 45),
|
|
kikoo_print))
|
|
sys.stdout.flush()
|
|
self.step += 1
|
|
|
|
def end(self):
|
|
"""Prints a line return
|
|
|
|
"""
|
|
sys.stdout.write("\n")
|
|
sys.stdout.flush()
|
|
|
|
@static_var("styles", {})
|
|
def style(texte, what=[]):
|
|
"""
|
|
Pretty text is pretty
|
|
On peut appliquer plusieurs styles d'affilée, ils seront alors traités
|
|
de gauche à droite (les plus à droite sont donc ceux appliqués en dernier,
|
|
et les plus à gauche en premier, ce qui veut dire que les plus à droite
|
|
viennent après, et non avant, de fait, comme ils arrivent après, ils
|
|
n'arrivent pas avant, et du coup, ceux qui arrivent avant peuvent être
|
|
écrasés par ceux qui arrivent après…
|
|
|
|
Tout ça pour dire que style(texte, ['vert', 'blanc', 'bleu', 'kaki']) est
|
|
équivalent à style(texte, ['kaki']) qui équivaut à
|
|
KeyError Traceback (most recent call last)
|
|
<ipython-input-2-d9c32f2123f0> in <module>()
|
|
----> 1 styles['kaki']
|
|
|
|
KeyError: 'kaki'
|
|
Sinon, il est possible de changer la couleur de fond grace aux couleur
|
|
f_<couleur>, et de mettre du GRAS (je songe encore à un module qui fasse du
|
|
UPPER, parce que c'est inutile, donc kewl.
|
|
|
|
"""
|
|
if isinstance(what, str):
|
|
what = [what]
|
|
# Pour l'éventuelle coloration du fond.
|
|
add = 0
|
|
|
|
if not what:
|
|
return texte
|
|
|
|
if style.styles == {}:
|
|
zeros = {'noir' : 30,
|
|
'rougefonce': 31,
|
|
'vertfonce' : 32,
|
|
'orange' : 33,
|
|
'bleufonce' : 34,
|
|
'violet' : 35,
|
|
'cyanfonce' : 36,
|
|
'grisclair' : 37,
|
|
}
|
|
f_zeros = { "f_"+coul : val+10 for (coul, val) in zeros.iteritems() }
|
|
zeros.update(f_zeros)
|
|
zeros = { coul: "0;%s" % (val,) for (coul, val) in zeros.items() }
|
|
|
|
ones = { 'gris': 30,
|
|
'rouge': 31,
|
|
'vert': 32,
|
|
'jaune': 33,
|
|
'bleu': 34,
|
|
'magenta': 35,
|
|
'cyan': 36,
|
|
'blanc': 37,
|
|
'gras': 50,
|
|
}
|
|
f_ones = { "f_"+coul : val+10 for (coul, val) in ones.iteritems() }
|
|
ones.update(f_ones)
|
|
ones = { coul: "1;%s" % (val,) for (coul, val) in ones.items() }
|
|
|
|
style.styles.update(zeros)
|
|
style.styles.update(ones)
|
|
for element in what:
|
|
texte = "\033[%sm%s\033[1;0m" % (style.styles[element], texte)
|
|
return texte
|
|
|
|
# WARNING! TOO MANY UPPER CASES.
|
|
OK = style('Ok', 'vert')
|
|
WARNING = style('Warning', 'jaune')
|
|
ERREUR = style('Erreur', 'rouge')
|
|
|
|
def prettyDoin(what, status):
|
|
"""Affiche une opération en cours et met son statut à jour
|
|
|
|
"""
|
|
if status == "...":
|
|
sys.stdout.write("\r[%s] %s" % (style(status, "jaune"), what))
|
|
elif status == "Ok":
|
|
sys.stdout.write("\r[%s] %s\n" % (OK, what))
|
|
elif status == "Warning":
|
|
sys.stdout.write("\r[%s] %s\n" % (WARNING, what))
|
|
else:
|
|
sys.stdout.write("\r[%s] %s\n" % (ERREUR, what))
|
|
sys.stdout.flush()
|
|
|
|
if __name__ == "__main__":
|
|
import time
|
|
a = Animation(texte="Test de l'animation", nb_cycles=10000, couleur=True, kikoo=True)
|
|
for i in range(0, a.nb_cycles):
|
|
time.sleep(0.0001)
|
|
a.new_step()
|
|
a.end()
|
|
prettyDoin("Je cuis des carottes.", "...")
|
|
time.sleep(1)
|
|
prettyDoin("Les carottes sont cuites." , "Ok")
|