Search code examples
pythoncurses

Python Curses Border on Resize


I am building a simple scrollable menu in Python 2.7 with curses and trying to make it scale for any window size, including if the terminal size changes (i.e. I want it to be larger or smaller while I'm using the menu). I have a simple test code that I'm using to try and figure out this problem. It seems, if this is possible, I am really close, but what I am seeing is that when I resize my terminal window (using mRemoteNG), the border draws lines to fill in space, as seen at the bottom of the screenshot, after I extended the window vertically:

resize-test_border-lines

The code I am using to test this is as follows:

import curses
import os

VERSION = "0.1-dev" #version number

screen = curses.initscr() #initialize the curses window

#Configure color pairs for showing select menu options as highlighted
curses.start_color() #enable color for highlighting menu options
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE) #color pair 1
highlightText = curses.color_pair(1) #color pair for highlighted menu option
normalText = curses.A_NORMAL #color pair for non-highlighted menu options

#Configure global variables for Curses
curses.noecho() #disable the keypress echo to prevent double input
curses.cbreak() #disable line buffers to run the keypress immediately
curses.curs_set(0)
screen.keypad(1) #enable keyboard use
screen.addstr(2, 2, "Screen Resize Test" + VERSION, curses.A_UNDERLINE)

#test screen resize
def main_screen():
    escape = False
    while escape == False:
        maxY, maxX = screen.getmaxyx()
        screen.border('|', '|', '-', '-', '+', '+', '+', '+')
        screen.addstr(4, 2, "MaxY: " + str(maxY))
        screen.addstr(5, 2, "MaxX: " + str(maxX))
        screen.refresh()

        x = screen.getch()

        if x == ord("q"):
            escape = True




main_screen()


curses.endwin() # *** CRITICAL *** this closes the curses menu and returns user to bash
os.system('clear') #clears the screen to avoid curses remnants

I've tried many different placements of screen.refresh() and screen.clear(), but it never seems to get rid of the remnants of lines at the edges of the curses window. Obviously, if this is possible, I either can't figure out where to place one/both of these or I'm not on the right track.


Solution

  • In your program, you should check for curses.KEY_RESIZE as a return value from getch, and in that case, call screen.erase.

    Also, the existing call to screen.refresh is unnecessary, since screen.getch does that anyway.

    This works for me:

    import curses
    import os
    
    VERSION = "0.1-dev" #version number
    
    screen = curses.initscr() #initialize the curses window
    
    #Configure color pairs for showing select menu options as highlighted
    curses.start_color() #enable color for highlighting menu options
    curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE) #color pair 1
    highlightText = curses.color_pair(1) #color pair for highlighted menu option
    normalText = curses.A_NORMAL #color pair for non-highlighted menu options
    
    #Configure global variables for Curses
    curses.noecho() #disable the keypress echo to prevent double input
    curses.cbreak() #disable line buffers to run the keypress immediately
    curses.curs_set(0)
    screen.keypad(1) #enable keyboard use
    screen.addstr(2, 2, "Screen Resize Test" + VERSION, curses.A_UNDERLINE)
    
    #test screen resize
    def main_screen():
        escape = False
        while escape == False:
            maxY, maxX = screen.getmaxyx()
            screen.border('|', '|', '-', '-', '+', '+', '+', '+')
            screen.addstr(4, 2, "MaxY: " + str(maxY))
            screen.addstr(5, 2, "MaxX: " + str(maxX))
    
            x = screen.getch()
    
            if x == ord("q"):
                escape = True
                curses.endwin()
            elif x == curses.KEY_RESIZE:
                screen.erase()
                screen.addstr(2, 2, "Screen Resize Test" + VERSION, curses.A_UNDERLINE)
    
    main_screen()