7

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?

ACarter
  • 5,688
  • 9
  • 39
  • 56
  • 1
    Try paring your code down to the minimum necessary to reproduce the bug. For example you could set nodelay(), then go into an infinite loop that breaks and exits when you getch() a key. If the bug goes away in the minimum test case figure out what's changed. If that doesn't help post the failing test case. – Philip Dec 22 '12 at 18:11
  • @Philip good thinking, have done so, and posted the results. – ACarter Dec 22 '12 at 19:49

3 Answers3

8

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!

Philip
  • 470
  • 1
  • 3
  • 11
  • 1
    Solved. Adding the main wrapper allowed me to see that I was exiting becuase of an error when `getch()` returned nothing (becuase no input was available), and was tried to be put into `chr()`, which can't handle nothing. Thanks for you're help, this will be really useful when solving other bugs. – ACarter Dec 23 '12 at 10:04
1

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.

0

I see no difference when running your small test program with or without the sc.nodelay() line.

Neither case prints anything on the screen...

Roland Smith
  • 42,427
  • 3
  • 64
  • 94
  • The point is that for me, with the `sc.nodelay()`, a curses windows isn't even opened, and hence I can't do anything, as there is nothing to do it on. – ACarter Dec 22 '12 at 21:35