331 lines
10 KiB
Python
Executable file
331 lines
10 KiB
Python
Executable file
#!/usr/bin/env python
|
|
# -*- coding: iso-8859-15 -*-
|
|
|
|
""" Collection de fonction/classe pour avoir un bel affichage
|
|
|
|
Copyright (C) Frédéric Pauget
|
|
Licence : GPLv2
|
|
"""
|
|
|
|
import sys, sre, os, tempfile
|
|
|
|
# Détermination de l'encodage
|
|
try:
|
|
from locale import getpreferredencoding
|
|
encoding = getpreferredencoding()
|
|
except:
|
|
encoding = sys.stdin.encoding or "ISO-8859-15"
|
|
|
|
if 'TERM' in os.environ and os.environ['TERM'] != 'unknown':
|
|
el = os.popen('tput cr ; tput el').read()
|
|
else:
|
|
el = os.popen('tput -Tvt100 cr ; tput -Tvt100 el').read()
|
|
|
|
def dialog(backtitle,arg) :
|
|
""" Affiche la boite de dialogue défine avec les arguments fournis
|
|
(cf man dialog)
|
|
si tout se déroule bien retourne :
|
|
[ 0, [ reponse(s) ] ]
|
|
si annulatin retourne :
|
|
[ 1, [] ]
|
|
si appui sur ESC demande confirmation de l'abandon et exécute sys.exit(0)
|
|
si erreur dans les arguments raise RuntimeError
|
|
"""
|
|
f = tempfile.NamedTemporaryFile()
|
|
cmd = u'LANG=fr_FR@euro /usr/bin/dialog --backtitle "%s" --cancel-label "Retour" %s 2>%s' % (backtitle,arg,f.name)
|
|
res = os.system(cmd.encode('iso-8859-15','ignore'))
|
|
|
|
if res == 256 :
|
|
# Annuler
|
|
f.close()
|
|
return [ 1, [] ]
|
|
|
|
# Lecture du fichier de résultat et effacement
|
|
try:
|
|
result=f.readlines()
|
|
f.close()
|
|
except :
|
|
result = [ "n'importe quoi", '']
|
|
res = 65280
|
|
|
|
# Traitement
|
|
if res==65280 and result:
|
|
# Erreur dans les arguments
|
|
raise RuntimeError( arg, result[1].strip() )
|
|
elif res==65280 :
|
|
# Appui sur ESC
|
|
arg1 = u'--title "Annulation" --yesno "Quitter ?\nLes dernières modifications seront perdues." 6 48'
|
|
print backtitle
|
|
cmd = u'/usr/bin/dialog --backtitle "%s" %s' % (backtitle,arg1)
|
|
res = os.system(cmd.encode('iso-8859-15','ignore') )
|
|
if res==0 : sys.exit(0)
|
|
else : return dialog(backtitle,arg)
|
|
elif not result : result=['']
|
|
|
|
return [ 0, result ]
|
|
|
|
def coul(txt,col):
|
|
"""
|
|
Retourne la chaine donnée encadrée des séquences qui
|
|
vont bien pour obtenir la couleur souhaitée
|
|
Les couleur sont celles de codecol
|
|
Il est possible de changer la couleur de fond grace aux couleur f_<couleur>
|
|
"""
|
|
codecol={'rouge' : 31 , 'vert' : 32 , 'jaune' : 33 , 'bleu': 34 , 'violet' : 35 , 'cyan' : 36 , 'gris' : 30, 'gras' : 50}
|
|
try :
|
|
if col[:2]=='f_' : add=10; col=col[2:]
|
|
else : add=0
|
|
txt = "\033[1;%sm%s\033[1;0m" % (codecol[col]+add,txt)
|
|
finally :
|
|
return txt
|
|
|
|
OK = coul('OK','vert')
|
|
WARNING = coul('WARNING','jaune')
|
|
ERREUR = coul('ERREUR','rouge')
|
|
|
|
def cprint(txt,col):
|
|
print coul(txt,col)
|
|
|
|
def tableau(largeurs,data) :
|
|
"""
|
|
retourne une chaine formatée repésentant un tableau
|
|
largeur est la liste des largeurs des colones
|
|
data est une liste de tuples :
|
|
[ ( données entète), (données ligne1), .... ]
|
|
"""
|
|
sep_col = u'|'
|
|
|
|
# Si l'une des largeurs est '*', alors on la met la plus grande possible
|
|
if '*' in largeurs:
|
|
rows, cols = get_screen_size()
|
|
nlargeurs = []
|
|
for n in largeurs:
|
|
if n != '*':
|
|
nlargeurs.append(n)
|
|
else:
|
|
nlargeurs.append(max(cols - sum(filter(lambda x:x!='*',largeurs)) - len(largeurs) - 1, 3))
|
|
largeurs = nlargeurs
|
|
|
|
# Ligne de séparation entète corps
|
|
s=u'\n'
|
|
for l in largeurs :
|
|
s+= sep_col + u'-'*l
|
|
s += sep_col + u'\n'
|
|
|
|
nb_cols = len(largeurs)
|
|
|
|
# Remplissage tableau
|
|
f=u''
|
|
for ligne in data :
|
|
for i in range(0, nb_cols) :
|
|
f+= sep_col
|
|
# Centrage
|
|
l = len(sre.sub('\x1b\[1;([0-9]|[0-9][0-9])m','',ligne[i])) # Longeur sans les chaines de formatage
|
|
if l >= largeurs[i] :
|
|
f += ligne[i]
|
|
else :
|
|
n = largeurs[i] - l
|
|
f += u' '*(n/2) + ligne[i] + u' '*(n/2 + n%2)
|
|
f+= sep_col + u'\n'
|
|
|
|
# Final
|
|
f = f.replace(u'\n',s,1) # Insertion du séparateur entète - corps
|
|
return f[:-1] # Supression du \n final
|
|
|
|
def tableau_ng(data,titre=None,largeur=None,alignement=None,format=None) :
|
|
"""
|
|
Retourne une chaine formatée repésentant un tableau
|
|
|
|
data : liste de listes, chacune contenant les valeurs d'une ligne
|
|
|
|
titre : liste des titres
|
|
Si none, n'affiche pas de ligne de titre
|
|
|
|
largeur : liste des largeurs des colonnes, '*' met la plus grande
|
|
largeur possible.
|
|
Si None, réduit aux max chaque colonne
|
|
|
|
alignement : liste des alignements : c = centrer
|
|
g = gauche
|
|
d = droit
|
|
Si None, met c pour chaque colonne
|
|
|
|
format : liste des formats : s = string
|
|
o = octet
|
|
Si None, s pour chaque colonne
|
|
"""
|
|
sep_col = u'|'
|
|
if data :
|
|
nbcols = len(data[0])
|
|
elif titre :
|
|
nbcols = len(titre)
|
|
else :
|
|
return u'Aucune donnée'
|
|
|
|
# Formats
|
|
#########
|
|
if not format :
|
|
format = ['s'] * nbcols
|
|
|
|
def reformate (data, format) :
|
|
if format == 's' :
|
|
return str(data)
|
|
|
|
elif format == 'o' :
|
|
data = float(data)
|
|
if data > 1024**3 :
|
|
return str(round(data/1024**3,1))+'Go'
|
|
elif data > 1024**2 :
|
|
return str(round(data/1024**2,1))+'Mo'
|
|
elif data > 1024 :
|
|
return str(round(data/1024,1))+'ko'
|
|
else :
|
|
return str(round(data,1))+'o'
|
|
|
|
data = [ [ reformate(ligne[i],format[i]) for i in range(nbcols) ] for ligne in data ]
|
|
|
|
# Largeurs
|
|
##########
|
|
if not largeur :
|
|
largeur = [ max([len(sre.sub('\x1b\[1;([0-9]|[0-9][0-9])m','',ligne[i])) for ligne in data]) for i in range(nbcols) ]
|
|
elif '*' in largeur:
|
|
rows, cols = get_screen_size()
|
|
for i in range(nbcols) :
|
|
if largeur[i] in ['*',-1] :
|
|
largeur[i] = max(cols - sum([l for l in largeur if l != '*']) - nbcols - 1, 3)
|
|
break
|
|
|
|
# Alignement
|
|
############
|
|
if not alignement :
|
|
alignement = ['c'] * nbcols
|
|
|
|
def aligne (data, alignement, largeur) :
|
|
# Longeur sans les chaines de formatage
|
|
l = len(sre.sub('\x1b\[1;([0-9]|[0-9][0-9])m','',data))
|
|
|
|
# Alignement
|
|
if l > largeur :
|
|
# découpage d'une chaine trop longue
|
|
regexp = sre.compile('\x1b\[1;([0-9]|[0-9][0-9])m')
|
|
new_data = u''
|
|
new_len = 0
|
|
|
|
# On laisse la mise en forme et on coupe les caratères affichés
|
|
while True :
|
|
s = regexp.search(data)
|
|
if s and not s.start() :
|
|
# c'est de la mise en forme
|
|
new_data += data[:s.end()]
|
|
data = data[s.end():]
|
|
elif new_len < largeur - 1 :
|
|
# c'est un caratère normal, et il y a la place
|
|
new_data += data[0]
|
|
data = data[1:]
|
|
new_len += 1
|
|
else :
|
|
# c'est un caratère normal mais on a dépassé le max
|
|
data = data[1:]
|
|
if not data :
|
|
return new_data + '*'
|
|
|
|
elif l == largeur :
|
|
return data
|
|
|
|
elif alignement == 'g' :
|
|
return u' ' + data + u' '*(largeur-l-1)
|
|
|
|
elif alignement == 'd' :
|
|
return u' '*(largeur-l-1) + data + u' '
|
|
|
|
else :
|
|
return u' '*((largeur-l)/2) + data + u' '*((largeur-l+1)/2)
|
|
|
|
data = [ [ aligne(ligne[i],alignement[i],largeur[i]) for i in range(nbcols) ] for ligne in data ]
|
|
|
|
# Le titre
|
|
##########
|
|
if titre :
|
|
# ligne de titre
|
|
chaine = sep_col + sep_col.join([aligne(titre[i],'c',largeur[i]) for i in range(nbcols)]) + sep_col + u'\n'
|
|
# ligne de séparation
|
|
chaine += sep_col + u'+'.join([u'-'*largeur[i] for i in range(nbcols)]) + sep_col + u'\n'
|
|
else :
|
|
chaine = u''
|
|
|
|
# Les données
|
|
#############
|
|
chaine += u'\n'.join([sep_col + sep_col.join([ligne[i] for i in range(nbcols)]) + sep_col for ligne in data])
|
|
|
|
return chaine
|
|
|
|
def get_screen_size():
|
|
"""Retourne la taille de l'écran.
|
|
|
|
Sous la forme d'un tuble (lignes, colonnes)"""
|
|
|
|
try:
|
|
from termios import TIOCGWINSZ
|
|
from struct import pack, unpack
|
|
from fcntl import ioctl
|
|
s = pack("HHHH", 0, 0, 0, 0)
|
|
rows, cols = unpack("HHHH", ioctl(sys.stdout.fileno(), TIOCGWINSZ, s))[:2]
|
|
return (rows, cols)
|
|
except:
|
|
return (24, 80)
|
|
|
|
def prompt(prompt, defaut='', couleur='gras'):
|
|
u"""
|
|
Pose la question prompt en couleur (défaut gras), retourne la réponse.
|
|
"""
|
|
sys.stdout.write(coul(prompt, couleur).encode(encoding))
|
|
if defaut :
|
|
sys.stdout.write((" [%s]" % defaut).encode(encoding))
|
|
sys.stdout.write(" ".encode(encoding))
|
|
v = sys.stdin.readline().decode(encoding).strip()
|
|
if not v: v = defaut
|
|
return v
|
|
|
|
class anim :
|
|
""" Permet de créer une animation :
|
|
truc................./
|
|
truc.................-
|
|
truc.................\
|
|
truc.................|
|
|
ou une barre de progression si le nombre total d'itérations est founi.
|
|
"""
|
|
def __init__(self,truc,iter=0) :
|
|
""" Affichage de :
|
|
truc................."""
|
|
self.txt = truc + '.'*(45-len(truc))
|
|
self.c = 1
|
|
self.iter = iter
|
|
sys.stdout.write(self.txt)
|
|
sys.stdout.flush()
|
|
|
|
def reinit(self) :
|
|
""" Efface la ligne courrante et
|
|
affiche : truc................. """
|
|
sys.stdout.write(el + self.txt)
|
|
if self.iter :
|
|
sys.stdout.write(' '*28)
|
|
sys.stdout.write(el + self.txt)
|
|
sys.stdout.flush()
|
|
|
|
def cycle(self) :
|
|
""" Efface la ligne courrante et
|
|
affiche : truc..................?
|
|
? caratère variant à chaque appel """
|
|
sys.stdout.write(el + self.txt)
|
|
if self.iter!=0 :
|
|
sys.stdout.write('[')
|
|
av = float(self.c) / float(self.iter)
|
|
n = int(20 * av)
|
|
sys.stdout.write('='*n)
|
|
sys.stdout.write('>')
|
|
sys.stdout.write(' '*(20 - n))
|
|
sys.stdout.write('] %3i%%' % int(100 * av) )
|
|
else :
|
|
sys.stdout.write('/-\|'[self.c%4])
|
|
sys.stdout.flush()
|
|
self.c += 1
|