How to scroll text in Python/Curses subwindow? How to scroll text in Python/Curses subwindow? python python

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 and subpad 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 that newpad 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: