Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python's curses module does not refresh pad until first character received

I have the following code that allows you to scroll up and down a pad of text. Each time you scroll (i.e. handle a user input) the pad updates as expected. However, before the first key is pressed nothing is shown, despite that I'm calling pad.refresh() just as I do after each user input.

My code looks like this :

def main(self,stdscr):

    x,y = 20,150 # size of the window
    u,a = 10,20 # where to place window - up,across
    pad = curses.newpad(20,150) # nlines, ncols
    pad_pos = 0
    exit = False

    pad.addstr(0,0,str(self.all_results))

    while not exit:
        pad.addstr(0,0,str(self.format_results()))
        ++ stdscr.refresh()
        pad.refresh(pad_pos,10, u,a, x,y)

        -- cmd = stdscr.getch()
        ++ cmd = pad.getch()

        stdscr.nodelay(1)

        + pad.getch() - caused the screen not to update
        + stdscr.refresh() - no change

        if cmd != -1:
            + pad.getch() - - caused the screen not to update
            if  cmd == curses.KEY_DOWN:
                if pad_pos < 3:
                    pad_pos += 1
                try:
                    pad.refresh(pad_pos,0, u,a, x,y)
                except curses.error:
                    pass
            elif cmd == curses.KEY_UP:
                if pad_pos != 0:
                    pad_pos -= 1
                try:
                    pad.refresh(pad_pos,0, u,a, x,y)
                except curses.error:
                    pass

Edit : changes shown within code as to what has been tried (+,++,--)

like image 734
felix001 Avatar asked Aug 12 '14 15:08

felix001


People also ask

How do you clear the screen curse in Python?

To clear characters until the end of the line, use clrtoeol(), To clear characters until the end of the window, use clrtobot().

What is Stdscr in Python?

The stdscr object returned by the initscr() function is a window object that covers the entire screen. Many programs may need only this single window, but you might wish to divide the screen into smaller windows, in order to redraw or clear them separately.

How do I install a curse module in Python?

How do I install a curse module in Python? To install the regular curses extension, you only have to uncomment the corresponding line of Modules/Setup in your Python source tree, and recompile. You may have to tweak the required libraries, as described in the comment: # Lance's curses module.


2 Answers

There seems to be the (AFAIK undocumented) necessity to refresh the stdscr once, i.e. call stdscr.refresh(), before everything works as expected. I incorporated this into your example (which I previously had to make working at all - see below):

import curses

def main(stdscr):
    stdscr.keypad(True)
    stdscr.refresh() # This is the necessary initial refresh

    heigth, width = 20, 150 # size of the pad
    top, left = 10, 20 # where to place pad
    viewportHeight = 10
    scrollOffset = 0

    # generate a string to fill pad
    string = ''.join(chr(ord('a') + (x % 26)) for x in range(heigth*width-1))

    pad = curses.newpad(heigth, width)
    pad.addstr(string)
    pad.refresh(scrollOffset, 0, top, left, top + 10, left + width)

    cmd = stdscr.getch()
    while True:
        if  cmd == curses.KEY_DOWN and scrollOffset < heigth - viewportHeight - 1:
            scrollOffset += 1
        elif cmd == curses.KEY_UP and scrollOffset > 0:
            scrollOffset -= 1
        if cmd:
            pad.refresh(scrollOffset, 0, top, left, top + 10, left + width)
        cmd = stdscr.getch()

curses.wrapper(main)

I hope this solves your problem!

A few remarks:

  • For the future I recommend you to use the curses.wrapper(main)-function which sets up the curses environment and calls your main(stdscr) method with the stdscr as parameter. It also takes care to tear down curses appropriately, which prevents a broken terminal if something goes wrong in your code. Maybe you want to check out the helpful tutorial by Kuchling and Raymond on how to use the python curses package.

  • Please provide a reproducible example. Yours contains variables such as self.all_result where I had to make guesses in order to understand your question. In fact I spent more time figure out what your code is trying to achieve exactly and to reproduce the erroneous behavior described by you, than to find the actual solution.

If you edit your question to bring it down to the actual problem - I think it could be very helpful to many people using Python's curses module.

Thanks & happy hacking ;)

Edit: I just realized you offered a bounty for a response drawing from official sources, so I dug around a little bit more and found an equivalent response on SO, that also suggests, that this - let's call it bug - is undocumented so far. My best guess for the reason doing some libncurses research, would be that the curses.wrapper in my case, you manually, or even libnurses by itself called an clear screen method at any point during the initialization. Thus, the clear flag might be set, which consequently clears the screen on the first refresh instead of the expected behavior.

like image 161
jan.vogt Avatar answered Oct 03 '22 14:10

jan.vogt


stdscr.getch() causes an implicit refresh of stdscr (not updated prior to this), which erases pad from the screen by overwriting it with the blank stdscr. Try pad.getch(), or else refresh stdscr before the first pad.refresh().

like image 34
William McBrine Avatar answered Oct 03 '22 12:10

William McBrine