This commit is contained in:
Pauline Pommeret 2015-02-15 05:00:03 +01:00
commit 5201e3dfdf
2 changed files with 167 additions and 110 deletions

View file

@ -6,29 +6,29 @@ Copyright (C) Frédéric Pauget
Licence : GPLv2 Licence : GPLv2
""" """
import sys
import re
import os import os
import tempfile import re
import shlex
import subprocess import subprocess
import sys
if not "/usr/scripts" in sys.path: if not "/usr/scripts" in sys.path:
sys.path.append("/usr/scripts") sys.path.append("/usr/scripts")
import cranslib.cransstrings import cranslib.cransstrings
# Détermination de l'encodage # Détermination de l'encodage
encoding = None ENCODING = None
try: try:
from locale import getpreferredencoding from locale import getpreferredencoding
encoding = getpreferredencoding() ENCODING = getpreferredencoding()
except: except:
pass pass
if not encoding: if not ENCODING:
encoding = sys.stdin.encoding or 'UTF-8' ENCODING = sys.stdin.encoding or 'UTF-8'
# Si aucune locale n'est définie, on se met en... # Si aucune locale n'est définie, on se met en...
if encoding == "ANSI_X3.4-1968": if ENCODING == "ANSI_X3.4-1968":
encoding = 'UTF-8' ENCODING = 'UTF-8'
if 'TERM' in os.environ and os.environ['TERM'] != 'unknown': if 'TERM' in os.environ and os.environ['TERM'] != 'unknown':
el = subprocess.Popen('tput cr ; tput el', shell=True, stdout=subprocess.PIPE).stdout.read() el = subprocess.Popen('tput cr ; tput el', shell=True, stdout=subprocess.PIPE).stdout.read()
@ -40,45 +40,90 @@ try:
except (IOError, AttributeError): except (IOError, AttributeError):
stdout_atty = False stdout_atty = False
def dialog(backtitle,arg,dialogrc='') : def dialog(backtitle, arg, dialogrc=''):
""" 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() Affiche la boîte de dialogue définie avec les arguments fournis
cmd = u'%s /usr/bin/dialog --backtitle "%s" --cancel-label "Retour" %s 2>%s' % (dialogrc, backtitle,arg,f.name) (cf man dialog)
res = os.system(cmd.encode(encoding, 'ignore'))
if res == 256 : Paramètres:
# Annuler - ``backtitle`` : (str) texte de l'en-tête, situé en haut à gauche
f.close() - ``arg`` : (str ou list) ensemble des paramètres à passer au
return [ 1, [] ] programme dialog
# Lecture du fichier de résultat et effacement - ``dialogrc`` : (str) chemin vers le dialogrc à utiliser
try:
result= f.readlines() Utilises:
f.close() - :py:var:`os.environ`
except : - :py:func:`shlex.split`
result = [ "n'importe quoi", ''] - :py:class:`subprocess.Popen`
res = 65280 - :py:func:`sys.exit`
# Traitement
if res==65280 and result: Peut lever:
# Erreur dans les arguments - :py:exc:`RuntimeError`
raise RuntimeError( arg, result[1].strip() )
elif res==65280 : Retournes:
# Appui sur ESC - soit un tuple dont le premier élément est le code d'erreur (0 ou 1) et
arg1 = u'--title "Annulation" --yesno "Quitter ?\nLes dernières modifications seront perdues." 6 48' le second la sortie du programme dialog (une liste ou une liste vide)
print backtitle - soit rien du tout parce que sys.exit(0)
cmd = u'%s /usr/bin/dialog --backtitle "%s" %s' % (dialogrc, backtitle,arg1) """
res = os.system(cmd.encode(encoding ,'ignore') ) # Tant que gest_crans (au moins) n'a pas été modifié pour que arg soit une
if res==0 : sys.exit(0) # liste, ce kludge est nécessaire.
else : return dialog(backtitle,arg) if not isinstance(arg, list):
elif not result : result=[''] arg = shlex.split(to_encoding(arg))
return [ 0, result ]
commande = [
"/usr/bin/dialog",
"--backtitle", backtitle,
"--cancel-label", "Retour"
] + arg
# On copie l'environnement afin de pouvoir écraser une variable pour
# l'appel au programme dialog sans que cela affecte les autres processus.
environnement = os.environ.copy()
# Si dialog est fourni, on modifie effectivement l'environnement
if dialogrc:
environnement["DIALOGRC"] = dialogrc
# Le résultat du programme dialog est dans stderr, on la récupère dans
# un pipe
processus = subprocess.Popen(commande, stderr=subprocess.PIPE, env=environnement)
# Récupération du contenu du pipe
_, sortie = processus.communicate()
resultat = sortie.splitlines()
# Récupération du code d'erreur
code_erreur = processus.returncode
if code_erreur == 1:
# L'utilisateur a annulé
return (1, [])
elif code_erreur == 255 and resultat:
# La liste des arguments passés au programme dialog est invalide
raise RuntimeError(arg, resultat[1].strip())
elif code_erreur == 255:
# L'utilisateur a appuyé sur ÉCHAP. Dans ce cas, on lui demande de
# confirmer.
# "0" et "0" sont là pour ajuster la taille de la fenêtre à son
# contenu
arg_confirmation = [
"--title", "Annulation",
"--yesno", "Quitter ?\nLes dernières modifications seront perdues.",
"0", "0"
]
code_confirmation, _ = dialog(backtitle, arg_confirmation, dialogrc)
if code_confirmation == 0:
# L'utilisateur veut quitter (vraiment).
sys.exit(0)
else:
# L'appui sur ÉCHAP était une fausse manip, on recommence
return dialog(backtitle, arg, dialogrc)
elif not resultat:
# Quand le code d'erreur est nul, il ne faut pas que resultat soit
# vide.
resultat = ['']
return (0, resultat)
def coul(txt, col=None, dialog=False): def coul(txt, col=None, dialog=False):
""" """
@ -90,23 +135,22 @@ def coul(txt, col=None, dialog=False):
if not stdout_atty or not col: if not stdout_atty or not col:
return txt return txt
codecol = { 'rouge': 31, codecol = {'rouge': 31,
'vert': 32, 'vert': 32,
'jaune': 33, 'jaune': 33,
'bleu': 34, 'bleu': 34,
'violet': 35, 'violet': 35,
'cyan': 36, 'cyan': 36,
'gris': 30, 'gris': 30,
'gras': 50 } 'gras': 50}
codecol_dialog = { codecol_dialog = {'rouge': 1,
'rouge': 1,
'vert': 2, 'vert': 2,
'jaune': 3, 'jaune': 3,
'bleu': 4, 'bleu': 4,
'violet': 5, 'violet': 5,
'cyan': 6, 'cyan': 6,
'gris': 0, 'gris': 0,
'gras': 'b' } 'gras': 'b'}
if dialog: if dialog:
try: try:
@ -127,7 +171,7 @@ OK = coul('OK', 'vert')
WARNING = coul('WARNING', 'jaune') WARNING = coul('WARNING', 'jaune')
ERREUR = coul('ERREUR', 'rouge') ERREUR = coul('ERREUR', 'rouge')
def to_unicode(txt, enc=encoding): def to_unicode(txt, enc=ENCODING):
if isinstance(txt, unicode): if isinstance(txt, unicode):
return txt return txt
else: else:
@ -139,7 +183,7 @@ def to_unicode(txt, enc=encoding):
# donc on le décode en utf-8 \o/ # donc on le décode en utf-8 \o/
return txt.decode("UTF-8") return txt.decode("UTF-8")
def to_encoding(txt, enc=encoding): def to_encoding(txt, enc=ENCODING):
return to_unicode(txt).encode(enc, 'ignore') return to_unicode(txt).encode(enc, 'ignore')
def cprint(txt, col='blanc', newline=True): def cprint(txt, col='blanc', newline=True):
@ -172,16 +216,16 @@ def tableau(data, titre=None, largeur=None, alignement=None, format=None, dialog
Si None, s pour chaque colonne Si None, s pour chaque colonne
""" """
sep_col = u'|' sep_col = u'|'
if data : if data:
nbcols = len(data[0]) nbcols = len(data[0])
elif titre : elif titre:
nbcols = len(titre) nbcols = len(titre)
else : else:
return u'Aucune donnée' return u'Aucune donnée'
# Formats # Formats
######### #########
if not format : if not format:
format = ['s'] * nbcols format = ['s'] * nbcols
def reformate(data, format): def reformate(data, format):
@ -192,7 +236,7 @@ def tableau(data, titre=None, largeur=None, alignement=None, format=None, dialog
sys.stderr.write("Cannot cast to unicode %r\n" % data) sys.stderr.write("Cannot cast to unicode %r\n" % data)
return unicode(data, errors='ignore') return unicode(data, errors='ignore')
elif format == 'o' : elif format == 'o':
data = float(data) data = float(data)
if data > 1024**3: if data > 1024**3:
return str(round(data/1024**3, 1)) + 'Go' return str(round(data/1024**3, 1)) + 'Go'
@ -203,12 +247,12 @@ def tableau(data, titre=None, largeur=None, alignement=None, format=None, dialog
else: else:
return str(round(data, 1)) + 'o' return str(round(data, 1)) + 'o'
data = [ [ reformate(ligne[i],format[i]) for i in range(nbcols) ] for ligne in data ] data = [[reformate(ligne[i], format[i]) for i in range(nbcols)] for ligne in data]
# Largeurs # Largeurs
########## ##########
if not largeur : if not largeur:
largeur = [ max([len(re.sub('\\\Z.' if dialog else '\x1b\[1;([0-9]|[0-9][0-9])m','',ligne[i])) for ligne in data]) for i in range(nbcols) ] largeur = [max([len(re.sub('\\\Z.' if dialog else '\x1b\[1;([0-9]|[0-9][0-9])m', '', ligne[i])) for ligne in data]) for i in range(nbcols)]
elif '*' in largeur: elif '*' in largeur:
if width: if width:
cols = width cols = width
@ -216,67 +260,67 @@ def tableau(data, titre=None, largeur=None, alignement=None, format=None, dialog
rows, cols = get_screen_size() rows, cols = get_screen_size()
if dialog: if dialog:
cols = cols - 6 cols = cols - 6
for i in range(nbcols) : for i in range(nbcols):
if largeur[i] in ['*',-1] : if largeur[i] in ['*', -1]:
largeur[i] = max(cols - sum([l for l in largeur if l != '*']) - nbcols - 1, 3) largeur[i] = max(cols - sum([l for l in largeur if l != '*']) - nbcols - 1, 3)
break break
# Alignement # Alignement
############ ############
if not alignement : if not alignement:
alignement = ['c'] * nbcols alignement = ['c'] * nbcols
def aligne (data, alignement, largeur) : def aligne(data, alignement, largeur):
# Longeur sans les chaines de formatage # Longeur sans les chaines de formatage
l = len(re.sub('\\\Z.' if dialog else '\x1b\[1;([0-9]|[0-9][0-9])m','',data)) l = len(re.sub('\\\Z.' if dialog else '\x1b\[1;([0-9]|[0-9][0-9])m', '', data))
# Alignement # Alignement
if l > largeur : if l > largeur:
# découpage d'une chaine trop longue # découpage d'une chaine trop longue
regexp = re.compile('\\\Z.' if dialog else '\x1b\[1;([0-9]|[0-9][0-9])m') regexp = re.compile('\\\Z.' if dialog else '\x1b\[1;([0-9]|[0-9][0-9])m')
new_data = u'' new_data = u''
new_len = 0 new_len = 0
# On laisse la mise en forme et on coupe les caratères affichés # On laisse la mise en forme et on coupe les caratères affichés
while True : while True:
s = regexp.search(data) s = regexp.search(data)
if s and not s.start() : if s and not s.start():
# c'est de la mise en forme # c'est de la mise en forme
new_data += data[:s.end()] new_data += data[:s.end()]
data = data[s.end():] data = data[s.end():]
elif new_len < largeur - 1 : elif new_len < largeur - 1:
# c'est un caratère normal, et il y a la place # c'est un caratère normal, et il y a la place
new_data += data[0] new_data += data[0]
data = data[1:] data = data[1:]
new_len += 1 new_len += 1
else : else:
# c'est un caratère normal mais on a dépassé le max # c'est un caratère normal mais on a dépassé le max
data = data[1:] data = data[1:]
if not data : if not data:
return new_data + '*' return new_data + '*'
elif l == largeur : elif l == largeur:
return data return data
elif alignement == 'g' : elif alignement == 'g':
return u' ' + data + u' '*(largeur-l-1) return u' ' + data + u' '*(largeur-l-1)
elif alignement == 'd' : elif alignement == 'd':
return u' '*(largeur-l-1) + data + u' ' return u' '*(largeur-l-1) + data + u' '
else : else:
return u' '*((largeur-l)/2) + data + u' '*((largeur-l+1)/2) 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 ] data = [[aligne(ligne[i], alignement[i], largeur[i]) for i in range(nbcols)] for ligne in data]
# Le titre # Le titre
########## ##########
if titre : if titre:
# ligne de 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' 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 # ligne de séparation
chaine += sep_col + u'+'.join([u'-'*largeur[i] for i in range(nbcols)]) + sep_col + u'\n' chaine += sep_col + u'+'.join([u'-'*largeur[i] for i in range(nbcols)]) + sep_col + u'\n'
else : else:
chaine = u'' chaine = u''
# Les données # Les données
@ -308,7 +352,7 @@ def prompt(prompt, defaut=u'', couleur='gras'):
defaut = cranslib.cransstrings.decode_dammit(defaut) defaut = cranslib.cransstrings.decode_dammit(defaut)
# coul renvoie alors un unicode # coul renvoie alors un unicode
prompt_s = coul(prompt, couleur) prompt_s = coul(prompt, couleur)
if defaut : if defaut:
prompt_s += u" [%s]" % defaut prompt_s += u" [%s]" % defaut
# On fait tout pour ne pas faire crasher le script appelant # On fait tout pour ne pas faire crasher le script appelant
# si on lève une erreur, on la rattrappe et on laisse une chance # si on lève une erreur, on la rattrappe et on laisse une chance
@ -316,7 +360,7 @@ def prompt(prompt, defaut=u'', couleur='gras'):
# On laisse néanmoins la possibilité de sortir sur un Ctrl+C # On laisse néanmoins la possibilité de sortir sur un Ctrl+C
while True: while True:
try: try:
v = raw_input(prompt_s.encode(encoding)) v = raw_input(prompt_s.encode(ENCODING))
v = cranslib.cransstrings.decode_dammit(v).strip() v = cranslib.cransstrings.decode_dammit(v).strip()
if not v: if not v:
v = defaut v = defaut
@ -327,7 +371,7 @@ def prompt(prompt, defaut=u'', couleur='gras'):
except UnicodeDecodeError as error2: except UnicodeDecodeError as error2:
print "UnicodeDecodeError catched but could not be displayed. Trying ASCII." print "UnicodeDecodeError catched but could not be displayed. Trying ASCII."
class anim : class anim:
""" Permet de créer une animation : """ Permet de créer une animation :
truc................./ truc................./
truc.................- truc.................-
@ -335,7 +379,7 @@ class anim :
truc.................| truc.................|
ou une barre de progression si le nombre total d'itérations est founi. ou une barre de progression si le nombre total d'itérations est founi.
""" """
def __init__(self,truc,iter=0) : def __init__(self, truc, iter=0):
""" Affichage de : """ Affichage de :
truc.................""" truc................."""
self.txt = truc + '.'*(45-len(truc)) self.txt = truc + '.'*(45-len(truc))
@ -344,27 +388,27 @@ class anim :
sys.stdout.write(self.txt) sys.stdout.write(self.txt)
sys.stdout.flush() sys.stdout.flush()
def reinit(self) : def reinit(self):
""" Efface la ligne courrante et """ Efface la ligne courrante et
affiche : truc................. """ affiche : truc................. """
sys.stdout.write(el + self.txt) sys.stdout.write(el + self.txt)
if self.iter : if self.iter:
sys.stdout.write(' '*28) sys.stdout.write(' '*28)
sys.stdout.write(el + self.txt) sys.stdout.write(el + self.txt)
sys.stdout.flush() sys.stdout.flush()
def cycle(self) : def cycle(self):
""" Efface la ligne courrante et """ Efface la ligne courrante et
affiche : truc..................? affiche : truc..................?
? caratère variant à chaque appel """ ? caratère variant à chaque appel """
if self.iter!=0 : if self.iter != 0:
prog = float(self.c) / float(self.iter) prog = float(self.c) / float(self.iter)
pprog = float(self.c-1) / float(self.iter) pprog = float(self.c-1) / float(self.iter)
n = int(20 * prog) n = int(20 * prog)
if 100*prog != 100*pprog: if 100*prog != 100*pprog:
msg = "%s[%s>%s] %3i%%" % (self.txt, '='*n, ' '*(20 - n), int (100*prog)) msg = "%s[%s>%s] %3i%%" % (self.txt, '='*n, ' '*(20 - n), int(100*prog))
sys.stdout.write(el + msg) sys.stdout.write(el + msg)
else : else:
sys.stdout.write(el + self.txt) sys.stdout.write(el + self.txt)
sys.stdout.write('/-\|'[self.c%4]) sys.stdout.write('/-\|'[self.c%4])
sys.stdout.flush() sys.stdout.flush()

View file

@ -53,19 +53,31 @@ isbureau = u'Bureau' in droits
encoding = sys.stdin.encoding or 'UTF-8' encoding = sys.stdin.encoding or 'UTF-8'
if u'Nounou' in droits: if u'Nounou' in droits:
# Si on est nounou
if os.path.exists(os.path.expanduser('~/.dialogrc')): if os.path.exists(os.path.expanduser('~/.dialogrc')):
dialogrc='~/.dialogrc' # Si on a un fichier de configuration du programme dialog dans son
# HOME, alors on récupère son chemin.
DIALOGRC = '~/.dialogrc'
else: else:
dialogrc='/etc/dialog.rc' # Sinon on utilise celui du système.
dlg = dialog.Dialog(DIALOGRC=dialogrc) DIALOGRC = '/etc/dialog.rc'
dialog_theme='DIALOGRC='+dialogrc
dlg = dialog.Dialog(DIALOGRC=DIALOGRC)
else: else:
# Si on est pas nounou, on est libre de faire ce que l'on veut avec la
# variable d'environnement DIALOGRC.
DIALOGRC = ''
dlg = dialog.Dialog() dlg = dialog.Dialog()
dialog_theme=''
def dialog(arg): def dialog(arg):
return affich_tools.dialog(u'Gestion des adhérents et machines du Crans', arg, dialog_theme) """
Raccourci permettant d'appeler :py:func:`affich_tools.dialog`.
.. seealso:: :py:func:`affich_tools.dialog`
"""
return affich_tools.dialog(u'Gestion des adhérents et machines du Crans', arg, DIALOGRC)
in_facture = None in_facture = None
######################################################################### #########################################################################
@ -92,7 +104,8 @@ def set_bases(adher):
# Affichage # Affichage
annul, result = dialog(arg) annul, result = dialog(arg)
if annul: return 1 if annul:
return 1
# Traitement # Traitement
err = '' err = ''