2

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.

  • Unrelated to your question: note that string.split() can more simply replace string.split(" "). – Eric O. Lebigot Aug 11 '09 at 07:48
  • 1
    @EOL: `string.split()` and `string.split(" ")` are not the same thing. – SilentGhost Aug 11 '09 at 08:42
  • It's cool, a guy with a 1 rep coming in, asling, taking then going away. love that. – Bite code Aug 18 '09 at 15:27
  • 3
    Hey e-satis, it seems like I have violated some rule of the community. Sorry. As you have observed, I'm new here and am not sure how things are run. I've been on vacation and have not been able to pursue the task that this question addresses so any feedback or thanks would have been uninformed. Could you point me to the equivalent of the etiquette FAQ? –  Aug 18 '09 at 21:42

3 Answers3

9

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
  • 854,459
  • 170
  • 1,222
  • 1,395
3

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.

Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
2

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.

Bite code
  • 578,959
  • 113
  • 301
  • 329