[gestion/affichage] Une tentative de créer un nouvel affich_tools.
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.
This commit is contained in:
parent
980afad15c
commit
a907a747af
1 changed files with 261 additions and 0 deletions
261
gestion/affichage.py
Executable file
261
gestion/affichage.py
Executable file
|
@ -0,0 +1,261 @@
|
|||
#!/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")
|
Loading…
Add table
Add a link
Reference in a new issue