Nov 06 2009

Python – Curses – Wrapper

Category: PythonFaltad @ 1:50 pm

Bonjour

La semaine passée, j’ai découvert et apprécié la toute puissance de Python.
Dans le cadre d’un mini projet, je me suis intéressé plus particulièrement à la bibliothèque curses.
Elle permet d’intéragir facilement avec son terminal, voir la page de la documentation pour plus d’information.

Alors, pour bien commencer, il faut importer curses, afin de pouvoir l’utiliser:

import curses

#On initialise curses
screen = curses.initscr()
screen.addstr(3, 3, "Hello World")
c = 0
#On boucle jusqu'à ce que l'utilisateur tappe la lettre 'q'.
#Attention, getch renvoit un int (pour les touches supérieures à 255
#Il faut donc convertir q en int
while c != ord('q'):
    c = screen.getch()

#ici on remet le terminal dans son état normal
curses.endwin()

Si vous essayer ce code, vous allez voir les caractères que vous tapez s’afficher à l’écran.
Il faut donc désactiver le mode echo du terminal.
Au passage, on peut empêcher la bufferisation du terminal ainsi que permettre à curses de gérer les
touches renvoyant plusieurs octets comme Home, End, Page Up, Page Down…

import curses

screen = curses.initscr()
#On initialise donc les options
curses.noecho()
curses.cbreak()
screen.keypad(1)

screen.addstr(3, 3, "Hello World")
c = 0
while c != ord('q'):
    c = screen.getch()

#Et on remet le term comme il faut
curses.echo()
curses.nocbreak()
screen.keypad(0)

curses.endwin()

Maintenant, un petit problème : si l’utilisateur appuie sur ctrl+c ou s’amuse à tuer notre application, elle ne réinitialisera pas le term… et il sera tout bousillé :(
La solution serait de faire comme ceci :
(On peut apprécier la belle syntaxe de Python sans accolade :) )

import curses
#On importe le module de gestion des erreurs
import traceback

#On definit une fonction qui reset le term, et si une erreur est definie
#on l'affiche.
def reset_term(screen, error = 0):
    curses.echo()
    curses.nocbreak()
    screen.keypad(0)
    curses.endwin()
    if error == 1:
        print traceback.format_exc()

screen = curses.initscr()
curses.noecho()
curses.cbreak()
screen.keypad(1)
#Initialisation des couleurs
curses.start_color()

#Hop, on attrape toutes les exceptions et on les renvoie
# sur la fonction reset_term.
try:
    #On initialise une paire de couleur.
    curses.init_pair(1, curses.COLOR_RED, 0)
    screen.addstr(3, 3, "Hello World", curses.color_pair(1))
    screen.refresh()
    c = 0
    while c != ord('q'):
        c = screen.getch()
    reset_term(screen)
except:
    reset_term(screen, 1)

Bon, le code commence à s’étoffer, avec presque que des initalisations et gestion d’erreur, ça fait beaucoup pour rien !
C’est là qu’intervient la fabuleuse fonction curses.wrapper !
Une petite explication s’impose :
curses.wrapper va s’occuper d’initialiser le terminal toute seule (avec la couleur aussi), et gérer les erreurs aussi.
Elle prends comme argument une fonction qu’elle s’occupera d’appeler une fois l’initialisation effectuée, ainsi que le nombre d’arguments que vous voulez lui envoyer.

import curses

# La fonction recevra obligatoirement une variable qui
# contiendra le retour de curses.initscr()
def main(screen, str):
    curses.init_pair(1, curses.COLOR_RED, 0)
    screen.addstr(3, 3, str, curses.color_pair(1))
    screen.refresh()
    c = 0
    while c != ord('q'):
        c = screen.getch()

curses.wrapper(main, str)

Beaucoup plus simple non ?
Voili voilou, premier article sur python soyez indulgent :)
Merci !

Tags: ,