[Python] Gestione tastiera in un loop

Manlio Perillo manlio_perillo a libero.it
Gio 26 Giu 2008 13:39:09 CEST


farolfo a hotmail.com ha scritto:
> --------------------------------------------------
> From: "Manlio Perillo" <manlio_perillo a libero.it>
> Sent: Thursday, June 26, 2008 12:28 PM
> To: "Discussioni generali sul linguaggio Python" <python a lists.python.it>
> Subject: Re: [Python] Gestione tastiera in un loop
> 
>> manlio_perillo a libero.it ha scritto:
>>
>> Ma stai usando .NET?
>> In questo caso non posso aiutarti, non lo conosco.
> 
> non uso .net e non lo o neanche usare :) Uso python 2.5 e PyScripter per 
> l'edit in Windows e ScrIDE in MacOS
> 

PhoneProfile.SerialPort sembra provenire da .NET.
Che package stai usando per comunicare con la seriale?

>> Ti consiglio di cominciare a leggere un tutorial su Python.
>> Questo codice intercetta un interrupt da tastiera (quando premi CTRL-C)
>> non la pressione di un tasto qualsiasi.
> 
> Ho letto "Pensare da informatico" di Allen B. Downey, Jeffrey Elkner e Chris 
> Meyers, pensavo intercettare la pressione dei tasti fosse una cosa più 
> semplice da fare, cmq seguiro i vostri consigli e mi documenterò meglio. 

E' abbastanza semplice, se hai un pò di esperienza.
Il problema è che l'unica cosa che è possibile fare in modo portabile è 
leggere una riga completa dalla console.

Ossia, se fai:
sys.stdin.read()

questa funziona ritorna solo dopo che l'utente preme il tasto [invio].

Per gestire un singolo tasto devi usare codice che dipende dalla 
piattaforma.


Eccoti due funzioni, per sistemi POSIX (Linux, OS X) e Windows.
Le due funzioni non sono equivalenti al 100%:


def posix_getch():
     "Si protrebbe usare direttamente tty.setcbreak() ma non c'è una
     funzione per ripristinare la modalità normale.
     """

     import termios, sys

     fd = sys.stdin.fileno()
     old = termios.tcgetattr(fd)
     new = termios.tcgetattr(fd)
     new[3] = new[3] & ~(termios.ECHO | termios.ICANON)  # lflags

     try:
         termios.tcsetattr(fd, termios.TCSAFLUSH, new)
         ch = sys.stdin.read(1)
     finally:
         termios.tcsetattr(fd, termios.TCSAFLUSH, old)

     return ch

def win_getch():
     import msvcrt

     return getch()


Queste due funzioni non ti bastano.
Quando fai:
PhoneProfile.SerialPort.readline()

la funzione non ritorna fino a quando non ha letto una riga completa 
dalla seriale.

Invece tu hai bisogno di monitorare sia la seriale che lo stdin allo 
stesso tempo.

Questo è un esempio di programmazione concorrente, non proprio banale.

Ci sono diverse soluzioni, la più elegante ed efficiente è usare l'IO 
multiplexing e non bloccante, ma avresti problemi ad implementarlo in 
modo semplice su Windows.

Quindi ti consiglio di usare un thread separato in cui fai:

def thread_main():
    # blocca fino a quanto non viene premuto un tasto
    getch()

    # lancia un KeybordInterrupt nel thread principale
    thread.interrupt_main()


In questo modo non devi modificare la struttura nel tuo codice 
principale, anche se questa soluzione è abbastanza limitata.

> Visto che cmq dovrei dotare il programma anche di un interfaccia grafica 
> vedro se le gui disponibili hanno queste funzioni.
> 

Con una GUI le cose si complicano, cerca di fare un passo alla volta.

> grazie
> 
> Luca 
> 


Manlio Perillo


Maggiori informazioni sulla lista Python