Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make curses program output persist in terminal scrollback history after program exits

I'm quite new to curses, so I'm trying out some different things in python.

I've initialized the window and set scrollok for the window object. I can add strings, and the scrolling works so that addstr() doesn't have any errors at the end of the window.

What I'd like to have is the ability to scroll back in the program output in my terminal program (tmux or KDE Konsole, in this case) after the program has finished.

In my code, I can at least see the output if I skip the endwin() call, but then the terminal needs a reset call to get back to operational.

Also, even while the program is running, after the curses window has scrolled down, I can't scroll back in Konsole to see the initial output.

#!/usr/bin/env python2
import curses
import time
win = curses.initscr()
win.scrollok(True)
(h,w)=win.getmaxyx()
h = h + 10
while h > 0:
    win.addstr("[h=%d] This is a sample string.  After 1 second, it will be lost\n" % h)
    h = h - 1
    win.refresh()
    time.sleep(0.05)
time.sleep(1.0)
curses.endwin()
like image 779
Wade Avatar asked Jan 21 '13 20:01

Wade


1 Answers

For this task, I would suggest that you use a pad (http://docs.python.org/2/library/curses.html#curses.newpad):

A pad is like a window, except that it is not restricted by the screen size, and is not necessarily associated with a particular part of the screen. [...] only a part of the window will be on the screen at one time. [...]

In order to leave the contents of the pad on the console after you have finished using curses, I would read the contents back in from the pad, end curses and write the contents to the standard output.

The following code achieves what you describe.

#!/usr/bin/env python2

import curses
import time

# Create curses screen
scr = curses.initscr()
scr.keypad(True)
scr.refresh()
curses.noecho()

# Get screen width/height
height,width = scr.getmaxyx()

# Create a curses pad (pad size is height + 10)
mypad_height = height + 10
mypad = curses.newpad(mypad_height, width);
mypad.scrollok(True)
mypad_pos = 0
mypad_refresh = lambda: mypad.refresh(mypad_pos, 0, 0, 0, height-1, width)
mypad_refresh()

# Fill the window with text (note that 5 lines are lost forever)
for i in range(0, height + 15):
    mypad.addstr("{0} This is a sample string...\n".format(i))
    if i > height: mypad_pos = min(i - height, mypad_height - height)
    mypad_refresh()
    time.sleep(0.05)

# Wait for user to scroll or quit
running = True
while running:
    ch = scr.getch()
    if ch == curses.KEY_DOWN and mypad_pos < mypad_height - height:
        mypad_pos += 1
        mypad_refresh()
    elif ch == curses.KEY_UP and mypad_pos > 0:
        mypad_pos -= 1
        mypad_refresh()
    elif ch < 256 and chr(ch) == 'q':
        running = False

# Store the current contents of pad
mypad_contents = []
for i in range(0, mypad_height):
    mypad_contents.append(mypad.instr(i, 0))

# End curses
curses.endwin()

# Write the old contents of pad to console
print '\n'.join(mypad_contents)
like image 135
ilent2 Avatar answered Sep 28 '22 02:09

ilent2