views:

147

answers:

3

I have a script that runs continuously when invoked and every 5 minutes checks my gmail inbox. To get it to run every 5 minutes I am using the time.sleep() function. However I would like user to end the script anytime my pressing q, which it seems cant be done when using time.sleep(). Any suggestions on how i can do this?

Ali

A: 

If you really wanted to (and wanted to waste a lot of resources), you could cut your loop into 200 ms chunks. So sleep 200 ms, check input, repeat until five minutes elapse, and then check your inbox. I wouldn't recommend it, though.

While it's sleeping, though, the process is blocked and won't receive input until the sleep ends.

Oh, as an added note, if you hit the key while it's sleeping, it should still go into the buffer, so it'll get pulled out when the sleep ends and input is finally read, IIRC.

JoshD
What would be a better way to implement this script? So that it keeps running continuously and checks my gmail every 5 minutes.
Ali
Sleep 300 ms, check keyboard input, repeat these two steps 1000 times. Then check gmail.
JoshD
+1  A: 

You can use select() on sys.stdin combined with a timeout. Roughly speaking, your main loop will look like this (untested):

while True:
    r,w,e = select.select([sys.stdin], [], [], 600)
    if sys.stdin in r: # data available on sys.stdin
        if sys.stdin.read() == 'q':
            break
    # do gmail stuff

To be able to read a single character from stdin you will need to put stdin in unbuffered mode. An alternative is described here. If you want to keep things simple, just require the user to hit enter after the 'q'

The -u flag I mentioned earlier won't work: it may put pyton in unbuffered mode but not your terminal.

Alternatively, ncursus may be of help here. I'm merely hinting, I don't have much experience with this; if I want a fancy user interface, I'd use TkInter.

Ivo van der Wijk
Ivo... I missed your link the first time. My post has been copied / modified from the same source.However, I cant get python to operate expectedly in unbuffered mode using your piece of code... any idea what could be wrong with the -u option?
Rajan
-u won't work, I just tried. I updated the post. If you really want unbuffered, use the termios magic or (possibly) (n)curses
Ivo van der Wijk
select only works on sockets in windows
aaronasterling
+1  A: 

Ok. try this python code... (Tested in linux. Most probably wont work on Windows - thanks to Aaron's input on that)

This is derived (copied and modified) from http://code.activestate.com/recipes/572182-how-to-implement-kbhit-on-linux/


import sys, termios, atexit
from select import select

delay = 1 # in seconds - change this for your needs

# save the terminal settings
fd = sys.stdin.fileno()
new_term = termios.tcgetattr(fd)
old_term = termios.tcgetattr(fd)

# new terminal setting unbuffered
new_term[3] = (new_term[3] & ~termios.ICANON & ~termios.ECHO)

# switch to normal terminal
def set_normal_term():
    termios.tcsetattr(fd, termios.TCSAFLUSH, old_term)

# switch to unbuffered terminal
def set_curses_term():
    termios.tcsetattr(fd, termios.TCSAFLUSH, new_term)

def getch():
    return sys.stdin.read(1)

def kbhit():
    dr,dw,de = select([sys.stdin], [], [], delay)
    return dr <> []

def check_mail():
    print 'Checking mail'

if __name__ == '__main__':
    atexit.register(set_normal_term)
    set_curses_term()

    while 1:
        if kbhit():
            ch = getch()
            break
        check_mail()

    print 'done'
Rajan
unfortunatly `select` only works on sockets in windows. Yet another reason to avoid it.
aaronasterling
@Aaron: This piece of code works fine on my ubuntu machine. Or did you mean select works only on linux?
Rajan
I meant that it only works on (nix as you would expect it to. On windows, it only works on sockets. I just checked the manual though termios only works on unix too so it's a moot point. Your solution only works on unix +1
aaronasterling