2012-03-24 7 views
9

Sto giocando un po 'con Python e bestemmie.Python curses dilemma

Quando eseguo

import time 
import curses 

def main(): 
    curses.initscr() 
    curses.cbreak() 
    for i in range(3): 
     time.sleep(1) 
     curses.flash() 
     pass 
    print("Hello World") 
    curses.endwin() 

if __name__ == '__main__': 
    main() 

se aspetto tutto il percorso attraverso, curses.endwin() viene chiamato in modo tutto funziona bene. Tuttavia, se l'ho tagliato corto con Ctrl-C, curses.endwin() non viene mai chiamato in modo da rovinare la mia sessione terminale.

Qual è il modo corretto di gestire questa situazione? Come posso assicurarmi che non importa come cerco di terminare/interrompere il programma (ad esempio Ctrl-C, Ctrl-Z), non rovina il terminale?

risposta

8

Si potrebbe fare questo:

def main(): 
    curses.initscr() 

    try: 
     curses.cbreak() 
     for i in range(3): 
      time.sleep(1) 
      curses.flash() 
      pass 
     print("Hello World") 
    finally: 
     curses.endwin() 

O, più esattamente, fare un wrapper contesto:

class CursesWindow(object): 
    def __enter__(self): 
     curses.initscr() 

    def __exit__(self): 
     curses.endwin() 

def main(): 
    with CursesWindow(): 
     curses.cbreak() 
     for i in range(3): 
      time.sleep(1) 
      curses.flash() 
      pass 
     print("Hello World") 
0

È possibile:

  • avvolgere il codice in un blocco try/finally che chiama curses.endwin()
  • cattura il segnale di interruzione specificamente via la libreria signal
  • utilizzare la libreria atexit.

La prima opzione è probabilmente la più semplice per un caso di base (se non si esegue molto codice).

La seconda opzione è la più specifica, se si desidera fare qualcosa speciale per Ctrl + C.

L'ultima opzione è la più robusta se si desidera sempre eseguire determinate azioni di arresto, indipendentemente dal termine del programma.

+0

è 'atexit' davvero più robusto di' finally'? – asmeurer

+0

@asmeurer È più una questione di "finalmente" la versione richiede di garantire che tutto il codice del programma sia all'interno del blocco try. In, ad esempio, un ambiente multithread, non è necessariamente il caso. – Amber

1

Il mio consiglio: per scopi di test, chiama il tuo script usando un semplice script di shell wrapper; hanno lo script di shell eseguire un comando reset per portare le impostazioni del terminale di nuovo in uno stato utilizzabile:

#!/bin/sh 
eval "[email protected]" 
stty -sane 
reset 

... Call che, come run.sh ed essere felici. Questo dovrebbe eseguire il tuo comando quasi esattamente come farebbe la tua shell se avessi inserito gli argomenti come comando (più esattamente se avvolgi gli argomenti tra virgolette).

Per assicurarsi che il programma sarà lasciare il terminale in uno stato solido, a fronte di eccezioni non gestite fanno e terminazioni anomale ... utilizzare il metodo curses.wrapper() per chiamare il punto di ingresso di livello superiore (probabilmente main() o quello che main_curses_ui() scegli di implementare) o avvolgi il tuo codice nella tua sequenza di metodi curses.* per ripristinare la visibilità del cursore, ripristinare la modalità "cbreak" (input canonico/cucinato), ripristinare le normali impostazioni "echo" e qualsiasi altra cosa tu possa aver fatto.

È inoltre possibile utilizzare Python: atexit Handlers per registrare tutte le azioni di pulizia. Ma potrebbero ancora esserci casi in cui il codice non viene richiamato --- alcuni tipi di segnali non intercettabili e qualsiasi situazione in cui viene invocato os._exit().

Il mio piccolo wrapper di script shell dovrebbe essere abbastanza robusto anche in questi casi.

44

Ti credo sono alla ricerca di curses.wrapper Vedere http://docs.python.org/dev/library/curses.html#curses.wrapper

Lo farà curses.cbreak(), curses.noecho() e curses_screen.keypad (1) su init e invertire la tendenza in uscita, anche se l'uscita era un'eccezione.

Il vostro programma va come una funzione per l'involucro, ad esempio:

def main(screen): 
    """screen is a curses screen passed from the wrapper""" 
    ... 

if __name__ == '__main__': 
    curses.wrapper(main) 
+4

questa dovrebbe essere la risposta accettata. –

Problemi correlati