Reputation: 5697
I've written a curses program in python. It runs fine. However, when I use nodelay()
, the program exits straight away after starting in the terminal, with nothing shown at all (just a new prompt).
EDIT
This code will reproduce the bug:
sc = curses.initscr() sc.nodelay(1) # But removing this line allows the program to run properly for angry in range(20): sc.addstr(angry, 1, "hi")
Here's my full code
import curses, time, sys, random def paint(x, y, i): #... def string(s, y): #... def feed(): #... sc = curses.initscr() curses.start_color() curses.curs_set(0) sc.nodelay(1) ######################################### # vars + colors inited for angry in range(20): try: dir = chr(sc.getch()) sc.clear() feed() #lots of ifs body.append([x, y]) body.pop(0) for point in body: paint(*point, i=2) sc.move(height-1, 1) sc.refresh() time.sleep(wait) except Exception as e: print sys.exc_info()[0], e sc.getch() curses.beep() curses.endwin()
Why is this happenning, and how can I use nodelay()
safely?
Upvotes: 7
Views: 7251
Reputation: 11
While I didn't use curses in python, I am currently working with it in C99, compiled using clang on Mac OS Catalina. It seems that nodelay()` does not work unless you slow down the program step at least to 1/10 of a second, eg. usleep(100000). I suppose that buffering/buffer reading is not fast enough, and getch() or wgetch(win*) simply doesn't manage to get the keyboard input, which somehow causes it to fail (no message whatsoever, even a "Segmentation fault").
For this reason, it's better to use halfdelay(1), which equals nodelay(win*, true) combined with usleep(100000).
I know this is a very old thread (2012), but the problem is still present in 2022, so I decided to reply.
Upvotes: 1
Reputation: 470
I've rewritten your minified demo to get the basic functionality working. It's got a nonblocking getch(). If you're holding the Q key when getch() is called, the program ends, otherwise the loop keeps going.
import curses, time
def main(sc):
sc.nodelay(1)
for angry in range(20):
sc.addstr(angry, 1, "hi")
sc.refresh()
if sc.getch() == ord('q'):
break
time.sleep(1)
if __name__=='__main__':
curses.wrapper(main)
The most significant change I made is using curses.wrapper to get a screen context instead of using curses.initscr(). The benefit is that if that if your program hits an uncaught exception (hitting ^C for example) it undos all the changes you did to the terminal like disabling the cursor before exiting. It helps a lot when you're debugging.
From here I'd recommend adding your program's features back in in very small steps. Curses is kind of a pain to work with and if you make a lot of changes at once it's hard to figure out which one caused things to break. Good luck!
Upvotes: 8
Reputation: 43495
I see no difference when running your small test program with or without the sc.nodelay()
line.
Neither case prints anything on the screen...
Upvotes: 0