Question

The code below lets you walk around a small grid on the screen using the arrow keys putting "." where you've explored or been next to. Even though I have my refresh before the first getch (to get a key-stroke) the screen doesn't first display anything until you've moved off your starting position. Shouldn't the addstr followed by refresh immediately show and then the getch waits after that? I even tried adding a stdscr.refresh(), but that didn't help either. How do I get the screen to refresh immediately before waiting for the first key-stroke?

import curses

def start(stdscr):
    curses.curs_set(0)
    movement = curses.newpad(10, 10)

    cur_x, cur_y = 5, 5

    while True:
        movement.addstr(cur_y, cur_x, '@')
        for (x_off, y_off) in [(-1,0),(1,0),(0,-1),(0,1)]:
            movement.addstr(cur_y + y_off, cur_x + x_off, '.')
        movement.refresh(1, 1, 0, 0, 7, 7) #Nothing is displayed until after the first key-stroke

        key_stroke = stdscr.getch()
        move_attempt = False
        if 0 < key_stroke < 256:
            key_stroke = chr(key_stroke)
        elif key_stroke == curses.KEY_UP and cur_y > 1:
            cur_y -= 1
        elif key_stroke == curses.KEY_DOWN and cur_y < 8:
            cur_y += 1
        elif key_stroke == curses.KEY_LEFT and cur_x > 1:
            cur_x -= 1
        elif key_stroke == curses.KEY_RIGHT and cur_x < 8:
            cur_x += 1
        else:
            pass

if __name__ == '__main__':
    curses.wrapper(start)
Was it helpful?

Solution

The docs are broken. I'd used curses back in the day, but libncurses is new to me.

My first hint came from ncurses(3):

The ncurses library permits manipulation of data structures, called windows, which can be thought of as two-dimensional arrays of characters representing all or part of a CRT screen. A default window called stdscr, which is the size of the terminal screen, is supplied. Others may be created with newwin. … Special windows called pads may also be manipulated. These are windows which are not constrained to the size of the screen and whose contents need not be completely displayed.

But then refresh(3) got decidedly evasive:

The routine wrefresh works by first calling wnoutrefresh, which copies the named window to the virtual screen, and then calling doupdate, which compares the virtual screen to the physical screen and does the actual update. … The phrase "copies the named window to the virtual screen" above is ambiguous. What actually happens is that all touched (changed) lines in the window are copied to the virtual screen. This affects programs that use overlapping windows; it means that if two windows overlap, you can refresh them in either order and the overlap region will be modified only when it is explicitly changed. [emphasis mine]

which prompted me to try adding

stdscr.refresh()

after your pad.refresh() which worked. And then I moved it further up start() to see if it was really needed on every pad modification. I moved it all the way up to the first point there is a stdscr to work with yielding:

def start(stdscr):
    stdscr.refresh()
    curses.curs_set(0)
    …

which smacks of voodoo programming, but I'm not going to look at the innards of a 20-year old library made to cope with glass ttys to try to grok it.

OTHER TIPS

Add stdscr.refresh() sometime before the movement.refresh() to solve the issue.

By adding time.sleep(1) after the refresh statement, it does write to the screen, but then it disappears when stdscr.getch() is called, but only the first time. Probably has to do with some sort of delayed initialization of stdscr.

Calling stdscr.refresh() after the movement.refresh() has the same effect: The very first time through the loop stdscr.refresh() clears the screen, but not in subsequent times through the loop. By calling stdscr.refresh() early in the program it gets this weird first time refresh out of the way.

When using a pad, for some reason—I don't know why—you have to call curses.doupdate after invoking the pad's refresh.

Adding window.nodelay(1) before while solved issue for me.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top