Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to scroll text in Python/Curses subwindow?

In my Python script which uses Curses, I have a subwin to which some text is assigned. Because the text length may be longer than the window size, the text should be scrollable.

It doesn't seem that there is any CSS-"overflow" like attribute for Curses windows. The Python/Curses docs are also rather cryptic on this aspect.

Does anybody here have an idea how I can code a scrollable Curses subwindow using Python and actually scroll through it?

\edit: more precise question

like image 705
lecodesportif Avatar asked Mar 25 '10 12:03

lecodesportif


People also ask

What is Stdscr in Python?

Starting and ending a curses application If successful, initscr() returns a window object representing the entire screen; this is usually called stdscr after the name of the corresponding C variable.

How do you use curse wrapper?

To use the wrapper, create a function that takes one argument: the screen. Then, call wrapper() and pass it your function that will operate with the screen. The wrapper() function takes care of initializing the curses screen that is normally done with curses. initscr() and also takes care of calling curses.


2 Answers

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 = 0 mypad.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) 
like image 192
lecodesportif Avatar answered Oct 02 '22 14:10

lecodesportif


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.7 import curses  # content - array of lines (list) mylines = ["Line {0} ".format(id)*3 for id in range(1,11)]  import pprint pprint.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:

  • (n)curses pad in python not working
  • http://threeseas.net/vic/vic/terminal.py
  • How do I delete a curse window in python and restore background window?
like image 22
sdaau Avatar answered Oct 02 '22 12:10

sdaau