Search code examples
pythonpython-curses

Python Curses addstr() error when keys are not pressed


I'm recreating this game for terminal in python using the curses library. Whenever a key is not pressed, stdscr.addstr() returns an error.

This seems to be because the cursor is off screen (in the bottom-right corner) during those frames, but I can't figure out why it moves at all. The canvas that I'm printing is always the same size (It consists entirely of spaces which are replaced when Apple or Basket objects are rendered). I tried decreasing the canvas size, but the cursor still goes to that corner. I also tried setting curses.leaveok(True), and the cursor seemed to be following the basket, but my logging proves that it is still going to that corner.

The file that uses curses:

import random
import time
import curses

from apple_season.basket import Basket
from apple_season.apple import Apple
from apple_season.coords import Canvas

def main(stdscr):

    curses.curs_set(1)  # so i can see where the cursor is
    dims = [curses.COLS - 1, curses.LINES - 1]  # pylint: disable=no-member

    stdscr.nodelay(True)
    stdscr.leaveok(True)
    key=""
    stdscr.clear()

    canvas = Canvas(*dims)

    basket = Basket(canvas)

    apples = []
    i = 0

    def finished_apples():
      if len(apples) <= 100:
         return False
      else:
         for apple in apples:
            if not apple.has_fallen:
               return False
         return True

    while not finished_apples():

        if len(apples) <= 100:  # don't make more if there are already 100
            # decide whether or not to create new apple (1/100 chance per frame)
            num = random.randint(0, 100)
            if num == 25:
                apples.append(Apple(canvas))

        try:
            key = stdscr.getkey()
            stdscr.clear()

            # pick up keyboard inputs
            # quit option
            if str(key) == "q":
                break

            # right arrow
            elif str(key) == "KEY_RIGHT":
                basket.move('right')

            # left arrow
            elif str(key) == "KEY_LEFT":
                basket.move('left')

        except Exception:
            pass

        # render objects - alters canvas to display them
        for apple in apples:
            if apple.has_fallen:
                apple.render()
            else:
                if '.0' not in str(i / 2):  # check if i is even (drop every other frame)
                    apple.fall()
                    apple.render()

        basket.render()

        try:
            stdscr.addstr(canvas.display)

        except Exception:
            pass

        stdscr.refresh()
        i += 1
        time.sleep(0.01)

if __name__ == "__main__":
    curses.wrapper(main)

(The code above runs fine, but it doesn't do anything when stdscr.addstr(canvas.display) doesn't work, which is whenever there are no keys pressed)

What's interesting is that this doesn't happen when it's just the basket or just the apples.

To see all of the code: https://github.com/lol-cubes/Terminal-Apple-Season/tree/soErrorCode.


Solution

  • I put the stdscr.clear() in the try and except block, which made it so that that code was only executed when a key was pressed, therefore overflowing the terminal because I was attempting to display multiple frames at once.