Search code examples
pythoncurses

How to implement long scroll menu in Python using Curses


I have a very big list in Python, and what I want to do, is to give the user the opportunity, to scroll through this list and check the records he wants.

In order to do that, I use Curses. The problem is - when the user presses the key up or down, the position of this "cursor" doesn't change, so, basically, by itself, the scroll works just fine, but the "cursor" just doesn't move.

Here is the code:

USERS = []

def pad_refresh(pad, pad_pos, height, width):
    pad.refresh(pad_pos, 0, 0, 0, height - 1, width)


def print_repositories_owners(stdscr, current_row_idx, height, width):
    stdscr.clear()
    print_menu_description(stdscr)
    for idx, row in enumerate(USERS):
        if idx == current_row_idx:
            stdscr.addstr(f'[x] {row}\n', curses.color_pair(1))
        else:
            stdscr.addstr(f'[] {row}\n')

    pad_refresh(stdscr, 0, height, width)


def navigate_repositories_owners(stdscr, current_row_idx, height, width):
    key = stdscr.getch()

    if key == curses.KEY_UP and current_row_idx > 0:
        current_row_idx -= 1
    elif key == curses.KEY_DOWN and current_row_idx < len(USERS) - 1:
        current_row_idx += 1
    elif key == curses.KEY_ENTER or key in [10, 13]:
        pass

    pad_refresh(stdscr, 0, height, width)
    print_repositories_owners(stdscr, current_row_idx, height, width)


def main(stdscr):
    height, width = stdscr.getmaxyx()
    pad_pos = 0
    pad = curses.newpad(PAD_HEIGHT, width)

    current_row_idx = 0

    print_repositories_owners(pad, current_row_idx, height, width)
    navigate_repositories_owners(pad, current_row_idx, height, width)

    pad_refresh(pad, pad_pos, height, width)

    key_up, key_down = 'AB'
    y = 0

    for c in iter(pad.getkey, 'q'):
        if c in '\x1b\x5b':
            continue
        y -= (c == key_up)
        y += (c == key_down)
        pad_refresh(pad, y, height, width)

if __name__ == '__main__':
    main(start)

To see how it works just fill USERS list with value and press the up and down keys. You will see, that the cursor on the first line doesn't move.

My guess is, this is because of the pad, that I am creating to put content there, and refresh - maybe I refresh the pad in a wrong way.

Thanks in advance.


Solution

  • I have found why it's not actually working. As you can see in main function to navigate_repositories_owners I pass pad as the first argument, and it was about this .getch() method. What I did, was just adding one more argument for this navigate_repositories_owners function and it was stdsrc.

    Basically, it allowed me to use my keyboard, also, in order to make this function work properly, I added while True statement. So, here is how it looks like now:

    def set_repositories_owner(stdscr):
        height, width = stdscr.getmaxyx()
        y = 0
        pad = curses.newpad(PAD_HEIGHT, width)
    
        current_row_idx = 0
    
        print_repositories_owners(pad, y, current_row_idx, height, width)
        navigate_repositories_owners(stdscr, pad, y, current_row_idx, height, width)
    
    ...
    
    def navigate_repositories_owners(stdscr, pad, pad_pos, current_row_idx, height, width):
        while True:
            key = stdscr.getch()
            ...
            print_repositories_owners(pad, pad_pos, current_row_idx, height, width)
            pad_refresh(pad, current_row_idx, height, width)