How to scroll text in Python/Curses subwindow?
OK with window.scroll it was too complicated to move the content of the window. Instead, curses.newpad did it for me.
Create a pad:
mypad = curses.newpad(40,60)mypad_pos = 0mypad.refresh(mypad_pos, 0, 5, 5, 10, 60)
Then you can scroll by increasing/decreasing mypad_pos depending on the input from window.getch() in cmd:
if cmd == curses.KEY_DOWN: mypad_pos += 1 mypad.refresh(mypad_pos, 0, 5, 5, 10, 60)elif cmd == curses.KEY_UP: mypad_pos -= 1 mypad.refresh(mypad_pos, 0, 5, 5, 10, 60)
Right, I was a bit confused on how to utilize pads (in order to scroll text), and still couldn't figure it out after reading this post; especially since I wanted to use it in a context of the content being an existing "array of lines". So I prepared a small example, that shows similarities (and differences) between newpad
and subpad
:
#!/usr/bin/env python2.7import curses# content - array of lines (list)mylines = ["Line {0} ".format(id)*3 for id in range(1,11)]import pprintpprint.pprint(mylines)def main(stdscr): hlines = begin_y = begin_x = 5 ; wcols = 10 # calculate total content size padhlines = len(mylines) padwcols = 0 for line in mylines: if len(line) > padwcols: padwcols = len(line) padhlines += 2 ; padwcols += 2 # allow border stdscr.addstr("padhlines "+str(padhlines)+" padwcols "+str(padwcols)+"; ") # both newpad and subpad are <class '_curses.curses window'>: mypadn = curses.newpad(padhlines, padwcols) mypads = stdscr.subpad(padhlines, padwcols, begin_y, begin_x+padwcols+4) stdscr.addstr(str(type(mypadn))+" "+str(type(mypads)) + "\n") mypadn.scrollok(1) mypadn.idlok(1) mypads.scrollok(1) mypads.idlok(1) mypadn.border(0) # first ... mypads.border(0) # ... border for line in mylines: mypadn.addstr(padhlines-1,1, line) mypadn.scroll(1) mypads.addstr(padhlines-1,1, line) mypads.scroll(1) mypadn.border(0) # second ... mypads.border(0) # ... border # refresh parent first, to render the texts on top #~ stdscr.refresh() # refresh the pads next mypadn.refresh(0,0, begin_y,begin_x, begin_y+hlines, begin_x+padwcols) mypads.refresh() mypads.touchwin() mypadn.touchwin() stdscr.touchwin() # no real effect here #stdscr.refresh() # not here! overwrites newpad! mypadn.getch() # even THIS command erases newpad! # (unless stdscr.refresh() previously): stdscr.getch()curses.wrapper(main)
When you run this, at first you will get something like (newpad
left, subpad
right):
┌────────────────────────┐ ┌────────────────────────┐ │Line 1 Line 1 Line 1 ───│ │Line 1 Line 1 Line 1 ───│ │Line 2 Line 2 Line 2 │ │Line 2 Line 2 Line 2 │ │Line 3 Line 3 Line 3 │ │Line 3 Line 3 Line 3 │ │Line 4 Line 4 Line 4 │ │Line 4 Line 4 Line 4 │ │Line 5 Line 5 Line 5 │ │Line 5 Line 5 Line 5 │ │Line 6 Line 6 Line 6 │ │Line 7 Line 7 Line 7 │ │Line 8 Line 8 Line 8 │ │Line 9 Line 9 Line 9 │ │Line 10 Line 10 Line 10 │ └────────────────────────┘
Some notes:
- Both
newpad
andsubpad
should have their width/height sized to the content (num lines/max line width of the array of lines) + eventual border space - In both cases, you could allow extra lines with
scrollok()
- but not extra width - In both cases, you basically "push" a line at the bottom of the pad; and then
scroll()
up to make room for the next - The special
refresh
method thatnewpad
has, then allows for just a region of this "whole content" to be shown on screen;subpad
more-less has to be shown in the size it was instantiated in - If you draw the borders of the pads before adding content strings - then the borders will scroll up too (that is the
───
piece shown at the...Line 1 ───│
part).
Useful links: