ACarter
ACarter

Reputation: 5697

nodelay() causes python curses program to exit

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

Answers (3)

Krzysztof Sobolewski
Krzysztof Sobolewski

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

Philip
Philip

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

Roland Smith
Roland Smith

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

Related Questions