tags:

views:

307

answers:

3

I'm trying to write a while loop that constantly updates the screen by using os.system("clear") and then printing out a different text message every few seconds. How do I get user input during the loop? raw_input() just pauses and waits, which is not the functionality I want.

import os
import time

string = "the fox jumped over the lazy dog"
words = string.split(" ")
i = 0 

while 1:
    os.system("clear")
    print words[i]
    time.sleep(1)
    i += 1
    i = i%len(words)

I would like to be able to press 'q' or 'p' in the middle to quit and pause respectively.

+5  A: 

The select module in Python's standard library may be what you're looking for -- standard input has FD 0, though you may also need to put a terminal in "raw" (as opposed to "cooked") mode, on unix-y systems, to get single keypresses from it as opposed to whole lines complete with line-end. If on Windows, msvcrt, also in Python standard library, has all the functionality you need -- msvcrt.kbhit() tells you if any keystroke is pending, and, if so, msvcrt.getch() tells you what character it is.

Alex Martelli
+3  A: 

You can also check one of the recipes available out there, which gives you the functionality you're looking for for both Unix and Windows.

EOL
A: 

You can do that with threads, here is a basic example :

import threading, os, time, itertools, Queue

# setting a cross platform getch like function
# thks to the Python Cookbook
# why isn't standard on this battery included language ?
try : # on windows
    from msvcrt import getch
except ImportError : # on unix like systems
    import sys, tty, termios
    def getch() :
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try :
            tty.setraw(fd)
            ch = sys.stdin.read(1)
        finally :
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch

# this will allow us to communicate between the two threads
# Queue is a FIFO list, the param is the size limit, 0 for infinite
commands = Queue.Queue(0)

# the thread reading the command from the user input     
def control(commands) :

    while 1 :

        command = getch()
        commands.put(command) # put the command in the queue so the other thread can read it

        #  don't forget to quit here as well, or you will have memory leaks
        if command == "q" :
            break


# your function displaying the words in an infinite loop
def display(commands):

    string = "the fox jumped over the lazy dog"
    words = string.split(" ")
    pause = False 
    command = ""

    # we create an infinite generator from you list
    # much better than using indices
    word_list = itertools.cycle(words) 

    # BTW, in Python itertools is your best friend

    while 1 :

        # parsing the command queue
        try:
           # false means "do not block the thread if the queue is empty"
           # a second parameter can set a millisecond time out
           command = commands.get(False) 
        except Queue.Empty, e:
           command = ""

        # behave according to the command
        if command == "q" :
            break

        if command == "p" :
            pause = True

        if command == "r" :
            pause = False

        # if pause is set to off, then print the word
        # your initial code, rewritten with a generator
        if not pause :
            os.system("clear")
            print word_list.next() # getting the next item from the infinite generator 

        # wait anyway for a second, you can tweak that
        time.sleep(1)



# then start the two threads
displayer = threading.Thread(None, # always to None since the ThreadGroup class is not implemented yet
                            display, # the function the thread will run
                            None, # doo, don't remember and too lazy to look in the doc
                            (commands,), # *args to pass to the function
                             {}) # **kwargs to pass to the function

controler = threading.Thread(None, control, None, (commands,), {})

if __name__ == "__main__" :
    displayer.start()
    controler.start()

As usual, using threads is tricky, so be sure you understand what you do before coding that.

Warning : Queue will be rename in queue in Python 3.

e-satis