[affich_tools] Remaniement de dialog
This commit is contained in:
parent
22c3d4eb71
commit
5d11ca086b
2 changed files with 167 additions and 110 deletions
|
@ -6,29 +6,29 @@ Copyright (C) Frédéric Pauget
|
|||
Licence : GPLv2
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import tempfile
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
if not "/usr/scripts" in sys.path:
|
||||
sys.path.append("/usr/scripts")
|
||||
import cranslib.cransstrings
|
||||
|
||||
# Détermination de l'encodage
|
||||
encoding = None
|
||||
ENCODING = None
|
||||
try:
|
||||
from locale import getpreferredencoding
|
||||
encoding = getpreferredencoding()
|
||||
ENCODING = getpreferredencoding()
|
||||
except:
|
||||
pass
|
||||
|
||||
if not encoding:
|
||||
encoding = sys.stdin.encoding or 'UTF-8'
|
||||
if not ENCODING:
|
||||
ENCODING = sys.stdin.encoding or 'UTF-8'
|
||||
|
||||
# Si aucune locale n'est définie, on se met en...
|
||||
if encoding == "ANSI_X3.4-1968":
|
||||
encoding = 'UTF-8'
|
||||
if ENCODING == "ANSI_X3.4-1968":
|
||||
ENCODING = 'UTF-8'
|
||||
|
||||
if 'TERM' in os.environ and os.environ['TERM'] != 'unknown':
|
||||
el = subprocess.Popen('tput cr ; tput el', shell=True, stdout=subprocess.PIPE).stdout.read()
|
||||
|
@ -40,45 +40,90 @@ try:
|
|||
except (IOError, AttributeError):
|
||||
stdout_atty = False
|
||||
|
||||
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
|
||||
def dialog(backtitle, arg, dialogrc=''):
|
||||
"""
|
||||
f = tempfile.NamedTemporaryFile()
|
||||
cmd = u'%s /usr/bin/dialog --backtitle "%s" --cancel-label "Retour" %s 2>%s' % (dialogrc, backtitle,arg,f.name)
|
||||
res = os.system(cmd.encode(encoding, 'ignore'))
|
||||
Affiche la boîte de dialogue définie avec les arguments fournis
|
||||
(cf man dialog)
|
||||
|
||||
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'%s /usr/bin/dialog --backtitle "%s" %s' % (dialogrc, backtitle,arg1)
|
||||
res = os.system(cmd.encode(encoding ,'ignore') )
|
||||
if res==0 : sys.exit(0)
|
||||
else : return dialog(backtitle,arg)
|
||||
elif not result : result=['']
|
||||
return [ 0, result ]
|
||||
Paramètres:
|
||||
- ``backtitle`` : (str) texte de l'en-tête, situé en haut à gauche
|
||||
- ``arg`` : (str ou list) ensemble des paramètres à passer au
|
||||
programme dialog
|
||||
- ``dialogrc`` : (str) chemin vers le dialogrc à utiliser
|
||||
|
||||
Utilises:
|
||||
- :py:var:`os.environ`
|
||||
- :py:func:`shlex.split`
|
||||
- :py:class:`subprocess.Popen`
|
||||
- :py:func:`sys.exit`
|
||||
|
||||
Peut lever:
|
||||
- :py:exc:`RuntimeError`
|
||||
|
||||
Retournes:
|
||||
- soit un tuple dont le premier élément est le code d'erreur (0 ou 1) et
|
||||
le second la sortie du programme dialog (une liste ou une liste vide)
|
||||
- soit rien du tout parce que sys.exit(0)
|
||||
"""
|
||||
# Tant que gest_crans (au moins) n'a pas été modifié pour que arg soit une
|
||||
# liste, ce kludge est nécessaire.
|
||||
if not isinstance(arg, list):
|
||||
arg = shlex.split(to_encoding(arg))
|
||||
|
||||
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):
|
||||
"""
|
||||
|
@ -90,23 +135,22 @@ def coul(txt, col=None, dialog=False):
|
|||
if not stdout_atty or not col:
|
||||
return txt
|
||||
|
||||
codecol = { 'rouge': 31,
|
||||
codecol = {'rouge': 31,
|
||||
'vert': 32,
|
||||
'jaune': 33,
|
||||
'bleu': 34,
|
||||
'violet': 35,
|
||||
'cyan': 36,
|
||||
'gris': 30,
|
||||
'gras': 50 }
|
||||
codecol_dialog = {
|
||||
'rouge': 1,
|
||||
'gras': 50}
|
||||
codecol_dialog = {'rouge': 1,
|
||||
'vert': 2,
|
||||
'jaune': 3,
|
||||
'bleu': 4,
|
||||
'violet': 5,
|
||||
'cyan': 6,
|
||||
'gris': 0,
|
||||
'gras': 'b' }
|
||||
'gras': 'b'}
|
||||
|
||||
if dialog:
|
||||
try:
|
||||
|
@ -127,7 +171,7 @@ OK = coul('OK', 'vert')
|
|||
WARNING = coul('WARNING', 'jaune')
|
||||
ERREUR = coul('ERREUR', 'rouge')
|
||||
|
||||
def to_unicode(txt, enc=encoding):
|
||||
def to_unicode(txt, enc=ENCODING):
|
||||
if isinstance(txt, unicode):
|
||||
return txt
|
||||
else:
|
||||
|
@ -139,7 +183,7 @@ def to_unicode(txt, enc=encoding):
|
|||
# donc on le décode en utf-8 \o/
|
||||
return txt.decode("UTF-8")
|
||||
|
||||
def to_encoding(txt, enc=encoding):
|
||||
def to_encoding(txt, enc=ENCODING):
|
||||
return to_unicode(txt).encode(enc, 'ignore')
|
||||
|
||||
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
|
||||
"""
|
||||
sep_col = u'|'
|
||||
if data :
|
||||
if data:
|
||||
nbcols = len(data[0])
|
||||
elif titre :
|
||||
elif titre:
|
||||
nbcols = len(titre)
|
||||
else :
|
||||
else:
|
||||
return u'Aucune donnée'
|
||||
|
||||
# Formats
|
||||
#########
|
||||
if not format :
|
||||
if not format:
|
||||
format = ['s'] * nbcols
|
||||
|
||||
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)
|
||||
return unicode(data, errors='ignore')
|
||||
|
||||
elif format == 'o' :
|
||||
elif format == 'o':
|
||||
data = float(data)
|
||||
if data > 1024**3:
|
||||
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:
|
||||
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
|
||||
##########
|
||||
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) ]
|
||||
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)]
|
||||
elif '*' in largeur:
|
||||
if width:
|
||||
cols = width
|
||||
|
@ -216,67 +260,67 @@ def tableau(data, titre=None, largeur=None, alignement=None, format=None, dialog
|
|||
rows, cols = get_screen_size()
|
||||
if dialog:
|
||||
cols = cols - 6
|
||||
for i in range(nbcols) :
|
||||
if largeur[i] in ['*',-1] :
|
||||
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 :
|
||||
if not alignement:
|
||||
alignement = ['c'] * nbcols
|
||||
|
||||
def aligne (data, alignement, largeur) :
|
||||
def aligne(data, alignement, largeur):
|
||||
# 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
|
||||
if l > largeur :
|
||||
if l > largeur:
|
||||
# découpage d'une chaine trop longue
|
||||
regexp = re.compile('\\\Z.' if dialog else '\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 :
|
||||
while True:
|
||||
s = regexp.search(data)
|
||||
if s and not s.start() :
|
||||
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 :
|
||||
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 :
|
||||
else:
|
||||
# c'est un caratère normal mais on a dépassé le max
|
||||
data = data[1:]
|
||||
if not data :
|
||||
if not data:
|
||||
return new_data + '*'
|
||||
|
||||
elif l == largeur :
|
||||
elif l == largeur:
|
||||
return data
|
||||
|
||||
elif alignement == 'g' :
|
||||
elif alignement == 'g':
|
||||
return u' ' + data + u' '*(largeur-l-1)
|
||||
|
||||
elif alignement == 'd' :
|
||||
elif alignement == 'd':
|
||||
return u' '*(largeur-l-1) + data + u' '
|
||||
|
||||
else :
|
||||
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 ]
|
||||
data = [[aligne(ligne[i], alignement[i], largeur[i]) for i in range(nbcols)] for ligne in data]
|
||||
|
||||
# Le titre
|
||||
##########
|
||||
if 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'
|
||||
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 :
|
||||
else:
|
||||
chaine = u''
|
||||
|
||||
# Les données
|
||||
|
@ -308,7 +352,7 @@ def prompt(prompt, defaut=u'', couleur='gras'):
|
|||
defaut = cranslib.cransstrings.decode_dammit(defaut)
|
||||
# coul renvoie alors un unicode
|
||||
prompt_s = coul(prompt, couleur)
|
||||
if defaut :
|
||||
if defaut:
|
||||
prompt_s += u" [%s]" % defaut
|
||||
# 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
|
||||
|
@ -316,7 +360,7 @@ def prompt(prompt, defaut=u'', couleur='gras'):
|
|||
# On laisse néanmoins la possibilité de sortir sur un Ctrl+C
|
||||
while True:
|
||||
try:
|
||||
v = raw_input(prompt_s.encode(encoding))
|
||||
v = raw_input(prompt_s.encode(ENCODING))
|
||||
v = cranslib.cransstrings.decode_dammit(v).strip()
|
||||
if not v:
|
||||
v = defaut
|
||||
|
@ -327,7 +371,7 @@ def prompt(prompt, defaut=u'', couleur='gras'):
|
|||
except UnicodeDecodeError as error2:
|
||||
print "UnicodeDecodeError catched but could not be displayed. Trying ASCII."
|
||||
|
||||
class anim :
|
||||
class anim:
|
||||
""" Permet de créer une animation :
|
||||
truc................./
|
||||
truc.................-
|
||||
|
@ -335,7 +379,7 @@ class anim :
|
|||
truc.................|
|
||||
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 :
|
||||
truc................."""
|
||||
self.txt = truc + '.'*(45-len(truc))
|
||||
|
@ -344,27 +388,27 @@ class anim :
|
|||
sys.stdout.write(self.txt)
|
||||
sys.stdout.flush()
|
||||
|
||||
def reinit(self) :
|
||||
def reinit(self):
|
||||
""" Efface la ligne courrante et
|
||||
affiche : truc................. """
|
||||
sys.stdout.write(el + self.txt)
|
||||
if self.iter :
|
||||
if self.iter:
|
||||
sys.stdout.write(' '*28)
|
||||
sys.stdout.write(el + self.txt)
|
||||
sys.stdout.flush()
|
||||
|
||||
def cycle(self) :
|
||||
def cycle(self):
|
||||
""" Efface la ligne courrante et
|
||||
affiche : truc..................?
|
||||
? caratère variant à chaque appel """
|
||||
if self.iter!=0 :
|
||||
if self.iter != 0:
|
||||
prog = float(self.c) / float(self.iter)
|
||||
pprog = float(self.c-1) / float(self.iter)
|
||||
n = int(20 * prog)
|
||||
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)
|
||||
else :
|
||||
else:
|
||||
sys.stdout.write(el + self.txt)
|
||||
sys.stdout.write('/-\|'[self.c%4])
|
||||
sys.stdout.flush()
|
||||
|
|
|
@ -53,19 +53,31 @@ isbureau = u'Bureau' in droits
|
|||
encoding = sys.stdin.encoding or 'UTF-8'
|
||||
|
||||
if u'Nounou' in droits:
|
||||
# Si on est nounou
|
||||
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:
|
||||
dialogrc='/etc/dialog.rc'
|
||||
dlg = dialog.Dialog(DIALOGRC=dialogrc)
|
||||
dialog_theme='DIALOGRC='+dialogrc
|
||||
# Sinon on utilise celui du système.
|
||||
DIALOGRC = '/etc/dialog.rc'
|
||||
|
||||
dlg = dialog.Dialog(DIALOGRC=DIALOGRC)
|
||||
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()
|
||||
dialog_theme=''
|
||||
|
||||
|
||||
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
|
||||
#########################################################################
|
||||
|
@ -92,7 +104,8 @@ def set_bases(adher):
|
|||
|
||||
# Affichage
|
||||
annul, result = dialog(arg)
|
||||
if annul: return 1
|
||||
if annul:
|
||||
return 1
|
||||
|
||||
# Traitement
|
||||
err = ''
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue