From ee134b4d84b41704a6133e0fe1d2aca56cb7f5db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 1 Jun 2014 02:08:29 +0200 Subject: [PATCH] [affichage] Fonction tableau et quelques correctifs --- gestion/affichage.py | 160 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 159 insertions(+), 1 deletion(-) diff --git a/gestion/affichage.py b/gestion/affichage.py index 199ed149..0bad2d88 100755 --- a/gestion/affichage.py +++ b/gestion/affichage.py @@ -39,6 +39,13 @@ import termios import struct import functools import time +import re + +oct_names = ["Pio", "Tio", "Gio", "Mio", "Kio", "o"] +oct_sizes = [1024**(len(oct_names) - i - 1) for i in xrange(0, len(oct_names))] +term_format = '\x1b\[[0-1];([0-9]|[0-9][0-9])m' +dialog_format = '\\\Z.' +sep_col = u"|" def static_var(name, val): """Decorator setting static variable @@ -205,8 +212,16 @@ class Animation(object): sys.stdout.write("\n") sys.stdout.flush() +def nostyle(dialog=False): + """ + Annule tout style précédent. + """ + if dialog: + return "\Zn" + return "\033[1;0m" + @static_var("styles", {}) -def style(texte, what=[]): +def style(texte, what=[], dialog=False): """ Pretty text is pretty On peut appliquer plusieurs styles d'affilée, ils seront alors traités @@ -228,6 +243,9 @@ def style(texte, what=[]): UPPER, parce que c'est inutile, donc kewl. """ + if dialog: + return dialogStyle(texte, what) + if isinstance(what, str): what = [what] @@ -264,6 +282,7 @@ def style(texte, what=[]): style.styles.update(zeros) style.styles.update(ones) + style.styles["none"] = "1;0" for element in what: texte = "\033[%sm%s\033[1;0m" % (style.styles[element], texte) return texte @@ -312,6 +331,140 @@ def prettyDoin(what, status): sys.stdout.write("\r[%s] %s\n" % (ERREUR, what)) sys.stdout.flush() +def tronque(data, largeur, dialog): + """ + Tronque une chaîne à une certaine longueur en excluant + les commandes de style. + """ + # découpage d'une chaine trop longue + regexp = re.compile(dialog_format if dialog else term_format) + 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: + if s.start() + new_len > largeur: + new_data += data[:largeur - new_len - 1] + nostyle() + '*' + break + else: + new_data += data[:s.end()] + data = data[s.end():] + new_len += s.start() + else: + if new_len + len(data) > largeur: + new_data += data[:largeur - new_len - 1] + '*' + data = "" + else: + new_data += data + data = "" + + if not data: + break + return new_data + +def aligne(data, alignement, largeur, dialog) : + # Longeur sans les chaines de formatage + longueur = len(re.sub(dialog_format if dialog else term_format, '', data)) + + # Alignement + if longueur > largeur: + return tronque(data, largeur, dialog) + + elif longueur == largeur : + return data + + elif alignement == 'g': + return u' ' + data + u' '*(largeur-longueur-1) + + elif alignement == 'd': + return u' '*(largeur-longueur-1) + data + u' ' + + else: + return u' '*((largeur-longueur)/2) + data + u' '*((largeur-longueur+1)/2) + +def format_data(data, format): + if format == 's': + return unicode(data) + + elif format == 'o': + data = float(data) + for i in xrange(0, len(oct_names)): + if data > oct_sizes[i]: + return "%.2f %s" % (data/oct_sizes[i], oct_names[i]) + +def tableau(data, titre=None, largeur=None, alignement=None, format=None, dialog=False, width=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 + width : force la largeur + """ + + if data and isinstance(data, list): + nb_cols = len(data[0]) + else: + return u"" + + if format is None: + format = ['s'] * nb_cols + + data = [[format_data(ligne[i], format[i]) for i in xrange(nb_cols)] for ligne in data] + + if not largeur : + largeur = [max([len(re.sub(dialog_format if dialog else term_format, '', ligne[i])) for ligne in data]) for i in range(nb_cols)] + elif '*' in largeur or -1 in largeur: + sum_larg = sum([l for l in largeur if l != '*']) + if width: + cols = width + else: + cols, rows = getTerminalSize() + if dialog: + cols = cols - 6 + stars = largeur.count('*') + largeur.count(-1) + for i in range(nb_cols): + if largeur[i] in ['*', -1] : + largeur[i] = max(cols - sum_larg - nb_cols - 1, 3)/stars + print largeur + + if alignement is None: + alignement = ['c'] * nb_cols + + data = [[aligne(ligne[i], alignement[i], largeur[i], dialog) for i in range(nb_cols)] for ligne in data] + + # Le titre + ########## + if titre : + # ligne de titre + chaine = sep_col + sep_col.join([aligne(titre[i], 'c', largeur[i], dialog) for i in range(nb_cols)]) + sep_col + u'\n' + # ligne de séparation + chaine += sep_col + u'+'.join([u'-'*largeur[i] for i in range(nb_cols)]) + 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(nb_cols)]) + sep_col for ligne in data]) + + return chaine + if __name__ == "__main__": import time a = Animation(texte="Test de l'animation", nb_cycles=10000, couleur=True, kikoo=True) @@ -322,3 +475,8 @@ if __name__ == "__main__": prettyDoin("Je cuis des carottes.", "...") time.sleep(1) prettyDoin("Les carottes sont cuites." , "Ok") + + data = [[style("Durand", "rouge"), u"Toto", u"40", u"50 rue caca"], [u"Dupont", "Robert", u"50", "42" + style(" avenue ", "vert") + style("dumotel", 'rouge')], [style("znvuzbvzruobouzb", ["gras", "vert"]), u"pppoe", u"1", u"poiodur 50 pepe"]] + titres = (u"Nom", u"Prénom", u"Âge", u"Adresse") + longueurs = [25, 25, '*', '*'] + print tableau(data, titres, longueurs)