Search code examples
pythonbordercurses

Why is my Curses border in Python disappearing?


I'm trying to get into Curses using Python and I'm fairly new to both subjects. I'm trying to make a scrollable menu object, and so far it looks decent but the issue I'm having is when scrolling through some test data, the window border disappears. I've tried window.clear() and window.refresh() throughout several points in the code but nothing I do makes the border persistent.

#!/usr/bin/python

import curses
import curses.panel

class CursesMenu():

    pos = 0
    c = None
    menuItems = None
    selection = None
    dimensions = None
    MAX_DISPLAYED = 5
    displayList = list(range(MAX_DISPLAYED))
    subwin_key_up = None
    subwin_key_down = None

    def __init__(self, menuItems=None):
        self.menuItems = menuItems
        curses.wrapper(self.main_menu, menuItems)

    def scrollDisplayUp(self): #shift each element of displayList down one
        for i in range(len(self.displayList)):
            self.displayList[i] -= 1

    def scrollDisplayDown(self): #shift each element of displayList up one
        for i in range(len(self.displayList)):
            self.displayList[i] += 1

    def posUp(self, win): # Lower pos = higher placement
        if self.pos == 0: #loop back around
            self.pos = len(self.menuItems) - 1
            self.displayList = list(range(len(self.menuItems) - self.MAX_DISPLAYED, len(self.menuItems)))
            win.clear()
        else:
            self.pos -= 1

    def posDown(self, win): # Higher pos = lower placement
        if self.pos >= len(self.menuItems) - 1: #loop back around
            self.pos = 0
            self.displayList = list(range(self.MAX_DISPLAYED))
            win.clear()
        else:
            self.pos += 1

    def main_menu(self, win, menuItems=None):
        self.dimensions = win.getmaxyx()
        curses.curs_set(0) #invisible cursor
        curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
        highlighted = curses.color_pair(1)
        normal = curses.A_NORMAL

        sub = win.subwin(self.MAX_DISPLAYED + 2, 20, 1, 1)
        sub.immedok(True) #jet fuel can't melt steel beams
        sub.box()

        while self.c != ord('\n'): #run until 'enter' gets pressed
            sub.box()
            self.dimensions = win.getmaxyx()

            if self.c == curses.KEY_UP:
                self.posUp(sub)
                if self.pos <= self.displayList[0] - 1:
                    self.scrollDisplayUp()
                    sub.clear()
                    sub.refresh()
            if self.c == curses.KEY_DOWN:
                self.posDown(sub)
                if self.pos >= self.displayList[-1] + 1:
                    self.scrollDisplayDown()
                    sub.clear()
                    sub.refresh()

            for i, j in zip(self.displayList, range(self.MAX_DISPLAYED)):
                if i != self.pos:
                    sub.addstr(j+1, 1, menuItems[i], normal)
                else:
                    sub.addstr(j+1, 1, menuItems[i], highlighted)

            self.c = win.getch()
        self.selection = menuItems[self.pos]

Here's a tiny script I'm using to test it:

#!/usr/bin/python

import cursesmenu

menulist=['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 'Hotel', \
'India', 'Juliett', 'Kilo', 'Lima', 'Mike', 'November', 'Oscar', 'Papa', 'Quebec', \
'Romeo', 'Sierra', 'Tango', 'Uniform', 'Victor', 'Whiskey', 'Xray', 'Yankee', 'Zulu']

m = cursesmenu.CursesMenu(menulist)
print m.selection
print m.dimensions

Solution

  • Thanks Anand, that solved the disappearing border, but then I found the stale text was sticking around. I fixed the issue by putting back the sub.clear() with sub.border(0) and sub.refresh() after.