Python et les interfaces consoles

Il est tout a fait possible en plus de la gestion d’argument (via ficher conf ou passage par la ligne de commande) de créer des interfaces consoles toutes à fait utilisables.

On peut interagir avec un terminal de plusieurs façons mais si il n’est pas possible sans ncurse d’avoir une interface du type vim

le grand avantage de ces interfaces sont:

  • rapidité de développement
  • utilisation de peu de ressource machine

Utilisation basique

la fonction raw_input permet en mode console l’attente d’une information utilisateur

mydata = raw_input('Prompt :')
print (mydata)
::
C:UsersfraoustinDesktop>python test3.py Prompt :coucou coucou

Création d’un interpréteur

Il faut pour cela utiliser le module cmd.Cmd

import cmd, sys

class HelloWorld(cmd.Cmd):
        """Simple command processor example."""
        prompt = '>>>'

        def preloop(self):
                print 'preloop()'

        def postloop(self):
                print 'postloop()'

        def do_greet(self, person):
                if person:
                        print "hi,", person
                else:
                        print 'hi'

        def help_greet(self):
                print '\n'.join([ 'greet [person]',
                        'Greet the named person',
                ])

        def do_test(self, line):
                print 'test'

        def default(self, line):
                print 'default(%s)' % line
                return cmd.Cmd.default(self, line)

        def do_exit(self, arg):
                """quit console"""
                sys.exit(1)

if __name__ == '__main__':
        HelloWorld().cmdloop()
C:\Users\fraoustin\Desktop>python test2.py
preloop()
>>>test
test
>>>greet tutu
hi, tutu
>>>help

Documented commands (type help <topic>):
========================================
exit  greet  help

Undocumented commands:
======================
test

>>>exit

Il est possible de modifier à volonté le comportement de l’interpreteur

Il est aussi possible de rajouter de la couleur dans le terminal en utilisant colorconsole http://code.google.com/p/colorconsole

Création d’interface évoluée

Pour cela il faut utiliser le module colorconsole http://code.google.com/p/colorconsole qui donne accès à de nombreux paramètres du terminal

le code suivant présente simplement l’affichage de l’heure de façon aléatoire sur l’ensemble du terminal avec un changement de couleur lui aussi aléatoire

import colorconsole, colorconsole.terminal
import sys
import datetime
import time
import random

list_color = colorconsole.terminal.colors.keys()
term = colorconsole.terminal.get_terminal()
term.set_title('test')
columns = term._Terminal__get_console_info().dwMaximumWindowSize.X
lines = term._Terminal__get_console_info().dwMaximumWindowSize.Y
term.clear()
term.reset()
try:
        while(True):
                now = datetime.datetime.now()
                x = random.randint(1,columns-20)
                y = random.randint(1,lines)
                fgcolorrandom = list_color[int(random.random() * len(list_color))]
                term.set_color(fg=colorconsole.terminal.colors[fgcolorrandom], bk=colorconsole.terminal.colors["BLACK"])
                term.print_at(x,y,now.strftime("%d/%m/%Y %H:%M:%S"))
                term.set_color(fg=colorconsole.terminal.colors["WHITE"], bk=colorconsole.terminal.colors["BLACK"])
                time.sleep(1)
                term.clear()
                term.reset()
except KeyboardInterrupt:
        pass
term.clear()
term.reset()
term.restore_buffered_mode()

Il est possible d’utiliser les fonctions raw_input pour avoir plus d’interaction ... mais cela impose d’arrêter le programme en cours

import colorconsole, colorconsole.terminal
import sys
import datetime
import time
import random

def randomColor():
        return list_color[int(random.random() * len(list_color))]

list_color = colorconsole.terminal.colors.keys()
term = colorconsole.terminal.get_terminal()
term.set_title('test')
columns = term._Terminal__get_console_info().srWindow.Right - term._Terminal__get_console_info().srWindow.Left +1
lines = term._Terminal__get_console_info().srWindow.Bottom - term._Terminal__get_console_info().srWindow.Top +1
fgcolorrandom = randomColor()
term.clear()
term.reset()
try:
        while(True):
                now = datetime.datetime.now()
                x = random.randint(1,columns-20)
                y = random.randint(1,lines)
                term.set_color(fg=colorconsole.terminal.colors[fgcolorrandom], bk=colorconsole.terminal.colors["BLACK"])
                term.print_at(x,y,now.strftime("%d/%m/%Y %H:%M:%S"))
                term.set_color(fg=colorconsole.terminal.colors["WHITE"], bk=colorconsole.terminal.colors["BLACK"])
                time.sleep(1)
                term.print_at(0,lines,'')
                if raw_input('changer de couleur') == 'c':
                        fgcolorrandom = randomColor()
                term.clear()
                term.reset()
except KeyboardInterrupt:
        pass
term.clear()
term.reset()
term.restore_buffered_mode()

il est possible de créer un listener qui attend les touches claviers

import colorconsole, colorconsole.terminal
import sys
import datetime
import time
import random
import msvcrt
import threading

list_color = colorconsole.terminal.colors.keys()
def randomColor():
        return list_color[int(random.random() * len(list_color))]

fgcolorrandom = randomColor()

def listen():
        global fgcolorrandom
        while 1:
                char = msvcrt.getch()
                if char == 'c':
                        fgcolorrandom = randomColor()

term = colorconsole.terminal.get_terminal()
term.set_title('test')
columns = term._Terminal__get_console_info().srWindow.Right - term._Terminal__get_console_info().srWindow.Left +1
lines = term._Terminal__get_console_info().srWindow.Bottom - term._Terminal__get_console_info().srWindow.Top +1
term.clear()
term.reset()

listener = threading.Thread(None, listen, None)
listener.start()

try:
        while(True):
                now = datetime.datetime.now()
                x = random.randint(1,columns-20)
                y = random.randint(1,lines)
                term.set_color(fg=colorconsole.terminal.colors[fgcolorrandom], bk=colorconsole.terminal.colors["BLACK"])
                term.print_at(x,y,now.strftime("%d/%m/%Y %H:%M:%S"))
                term.set_color(fg=colorconsole.terminal.colors["WHITE"], bk=colorconsole.terminal.colors["BLACK"])
                time.sleep(1)
                term.clear()
                term.reset()
except KeyboardInterrupt:
        pass
term.clear()
term.reset()
term.restore_buffered_mode()

il est donc possible d’imaginer un listener en forme de classe avec une gestion de file (via le module Queue)

les key-code a utiliser sont

Dec Abbr
0 Null character
1 Start of Header
2 Start of Text
3 End of Text
4 End of Transmission
5 Enquiry
6 Acknowledgment
7 Bell
8 Backspace[d][e]
9 Horizontal Tab[f]
10 Line feed
11 Vertical Tab
12 Form feed
13 Carriage return[g]
14 Shift Out
15 Shift In
16 Data Link Escape
17 Device Control 1 (oft. XON)
18 Device Control 2
19 Device Control 3 (oft. XOFF)
20 Device Control 4
21 Negative Acknowledgement
22 Synchronous idle
23 End of Transmission Block
24 Cancel
25 End of Medium
26 Substitute
27 Escape[i]
28 File Separator
29 Group Separator
30 Record Separator
31 Unit Separator
32  
33 !
34
35 #
36 $
37 %
38 &
39
40 (
41 )
42 *
43 +
44 ,
45 -
46 .
47 /
48 0
49 1
50 2
51 3
52 4
53 5
54 6
55 7
56 8
57 9
58 :
59 ;
60 <
61 =
62 >
63 ?
64 @
65 A
66 B
67 C
68 D
69 E
70 F
71 G
72 H et fleche Haut
73 I
74 J
75 K et fleche Gauche
76 L
77 M er fleche Droite
78 N
79 O
80 P et fleche Bas
81 Q
82 R
83 S
84 T
85 U
86 V
87 W
88 X
89 Y
90 Z
91 [
92
93 ]
94 ^
95 _
96 ```
97 a
98 b
99 c
100 d
101 e
102 f
103 g
104 h
105 i
106 j
107 k
108 l
109 m
110 n
111 o
112 p
113 q
114 r
115 s
116 t
117 u
118 v
119 w
120 x
121 y
122 z
123 {
124 pipe more
125 }
126 ~
127 DEL

Redimensionner la console windows

Il est parfois utile de pouvoir redimensionner la console windows.

Le script suivant fait cela très bien

def console_resize(width=80, height=24, buffer_height=600):
    '''Sets up the console size and buffer height.

    <at> param width {int} Width of console in column value.
    <at> param height {int} Height of console in row value.
    <at> param buffer_height {int} Buffer console height in row value.
    '''
    from ctypes import windll, byref, create_string_buffer
    from ctypes.wintypes import SMALL_RECT, _COORD
    # Active console screen buffer
    # STD_OUTPUT_HANDLE -> -11, STD_ERROR_HANDLE -> -12)
    STDERR = -12
    # SMALL_RECT input
    LEFT = 0
    TOP = 0
    RIGHT = width - 1
    BOTTOM = height - 1
    # handle
    hdl = windll.kernel32.GetStdHandle(STDERR)
    csbi = create_string_buffer(22)

    res = windll.kernel32.GetConsoleScreenBufferInfo(hdl, csbi)

    if res:
        import struct
        (bufx, bufy, curx, cury, wattr,
         left, top, right, bottom,
         maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)

    current_width = right - left + 1
    current_height = bottom - top + 1
    current_buffer_height = bufy

    if buffer_height < height:
        buffer_height = height
    # order of resizing avoiding some problems
    if current_buffer_height > buffer_height:
        rect = SMALL_RECT(LEFT, TOP, RIGHT, BOTTOM)  # (left, top, right, bottom)
        windll.kernel32.SetConsoleWindowInfo(hdl, True, byref(rect))

        bufsize = _COORD(width, buffer_height)  # columns, rows
        windll.kernel32.SetConsoleScreenBufferSize(hdl, bufsize)
    else:
        bufsize = _COORD(width, buffer_height)  # columns, rows
        windll.kernel32.SetConsoleScreenBufferSize(hdl, bufsize)

        rect = SMALL_RECT(LEFT, TOP, RIGHT, BOTTOM)  # (left, top, right, bottom)
        windll.kernel32.SetConsoleWindowInfo(hdl, True, byref(rect))

console_resize()