views:

679

answers:

4

I'm prototyping a Python app with the cmd module.

Some messages to the user will be quite long and I'd like to paginate them. The first 10 (or a configurable number) lines of the message would appear, and pressing the SPACE bar would display the next page, until the end of the message.

I don't want to reinvent something here, is there a simple mean to implement this feature?

+1  A: 

The simple thing would just be to pipe your script through "less" or a similar command at runtime.

Here's a simple method that does approximately what you want, though:

def print_and_wait(some_long_message):
    lines = some_long_message.split('\n')
    i=0
    while i < len(lines):
        print '\n'.join(lines[i:i+10])
        raw_input("press enter to read more...")
        i += 10

You could also look into using curses.

Yoni Samlan
Thanks, but piping through less would prevent interaction, which is the goal of the cmd module. Implementing a curses solution is far beyond the first draft I want to build with the cmd module.However your print_and_wait solution is a good start (didn't work as is though).
Gra
+1  A: 

As Yoni said above the right way to do this is to provide a print method that pages automatically inside your running cmd instance. The constructor of Cmd takes stdin and stdout arguments. So simple provide an object that works like stdout and supports your paging print method.

class PagingStdOut(object):
    def write(self, buffer, lines_before_pause=40):
        # do magic paging here...
Trey Stout
A: 

Paging subroutines can be found in the genutils.py file of IPython (see page, or page_dumb for a simpler one). The code is a little complicated, but that's probably unavoidable if you are trying to be portable to systems including Windows and the various kinds of terminal emulators.

Jouni K. Seppänen
Thanks, I already implemented my paging routines. I'll have a look to see what I missed in the files you point. Currently it works in my OS, but I didn't test anything else, so the portable enhancements could be useful.
Gra
A: 

I had the same question. There is a pager built in to the pydoc module. I incorporated it thusly (which I find hackish and unsatisfying... I'm open to better ideas though).

I like the idea that it would autopage if there are more than x results and paging is on, which is possible to implement, but not done here.

import cmd
from pydoc import pager
from cStringIO import StringIO
import sys

PAGER = True
class Commander(cmd.Cmd):
    prompt = "> "
    def do_pager(self,line):
        global PAGER
        line = line + " 1"
        tokens = line.lower().split()
        if tokens[0] in ("on","true","t", "1"):
            PAGER = True
            print "# setting PAGER True"
        elif tokens[0] in ("off","false","f","0"):
            PAGER = False
            print "# setting PAGER False"
        else:
            print "# can't set pager:  don't know -> %s" % tokens[0]

    def do_demo(self,line):
        results = dict(a=1,b=2,c=3)
        self.format_commandline_results(results)

    def format_commandline_results(self,results):
        if PAGER:
            ofh = StringIO()
        else:
            ofh = sys.stdout

        for (k,v) in sorted(results.items()):
            print >> ofh, "%s -> %s" % (k,v)

        if PAGER:
            ofh.seek(0)
            pager(ofh.read())

        return None

    def do_EOF(self,line):
        print "",
        return True

if __name__ == "__main__":
    Commander().cmdloop("# try: \n> pager off \n> demo \n> pager on \n> demo \n\n")
Gregg Lind