5

I've got a curses application that uses subwindows, but I can't seem to be able to delete them.

For example, this code doesn't work:

import curses
def fill(window, ch):
    y, x = window.getmaxyx()
    s = ch * (x - 1)
    for line in range(y):
        window.addstr(line, 0, s)

def main(stdscr):
    fill(stdscr, 'M')
    stdscr.refresh()
    stdscr.getch()

    subwin = stdscr.subwin(1, 28, 20, 13)
    fill(subwin, 'J')
    subwin.refresh()
    subwin.getch()

    del subwin
    stdscr.touchwin()
    stdscr.refresh()
    stdscr.getch()

curses.wrapper(main)

When you run this code, the screen fills with 'M', then when you hit a key, a subwindow is created and filled with 'J'. Finally, when you press a key again, the code deletes the subwindow and completely redraws the screen. However, those Js are still there.

After some experimentation, I've found that calling the clear() method of stdscr will make the subwindow go, but I would like to restore the background as it was, without blanking it and rewriting. Does anyone know a way in which this could be done?

MageJohn
  • 127
  • 2
  • 7
  • Anyone knows where this code comes from? Is [Alex the author](http://stackoverflow.com/a/2576084/923794)? Interestingly the only real difference is the `subwin`/`newwin` issue... – cfi Jun 05 '13 at 13:25
  • Yes, I did get the code from that question. I'd found it looking for an answer to mine. The code (like you noticed) only needs one function name changed to demonstrate my problem. I've now double checked the rules, and I probably should have given proper attribution. – MageJohn Jun 05 '13 at 20:12

2 Answers2

11

Is there a good reason why you're using a subwindow? If you create a new top-level window then the code works correctly - simply change stdscr.subwin to curses.newwin and it works as you'd expect.

I'm not a curses expert, but I believe a subwindow shares the character buffer with its parent such that changes to either one will also affect the other. So, if you're looking to sub-divide a window into logical areas (perhaps a menu bar, main area and status bar) then subwindows are useful. If, however, you're looking for something more like a dialog box or pop-up menu then a whole new window (with its own separate buffer) is what you're after.

I can't find any definitive reference for ncurses which agrees or disagrees with me, but man page for AIX seems to corroborate it:

Recall that the subwindow shares its parent's window buffer. Changes made to the shared window buffer in the area covered by a subwindow, through either the parent window or any of its subwindows, affects all windows sharing the window buffer.

Of course, this isn't definitive for ncurses, but I can't find anything to the contrary and it certainly seems to explain the behaviour observed. I also did a crude experiment where, immediately after the subwin.getch() line in your example, I added this line:

raise Exception(stdscr.instr(20, 15, 3))

In your example, I get JJJ as the content of the actual main window. If I change to use curses.newwin() to create the window instead of stdscr.subwin() I get the expected MMM.

I don't know how many specific Python curses resources there are, but most of the standard tutorials and documents about ncurses are quite useful for this sort of level. Back when I had to do some work in it, this document was quite useful. If you scroll down to the "An Example" section, you'll see that the menu pop-ups are not subwindows - he alludes to this with the following slightly vague explanation:

We don't want this new window to overwrite previously written characters on the background. They should stay there after the menu closes. This is why the menu window can't be created as a subwindow of stdscr.

Also, I remember that using both stdscr and your own windows can cause issues - the "official" ncurses introduction has some warnings about this sort of thing. It also suggests avoiding overlapping windows entirely, as they're apparently error-prone, but I don't recall having any issues with them for short-term transient modal dialogs (which is the only use to which I put them). Of course, just because my simple use-case didn't expose any issues doesn't mean there aren't any. In something as complicated as ncurses, however, I can see the wisdom in keeping things as simple as you can.

I hope that's some help. As I said, I'm by no means a curses expert, but hopefully this gets you a few steps further along.

Cartroo
  • 4,233
  • 20
  • 22
  • Thanks, this is does seem to be just the info I need. I had used subwin because it sounded like what I intended to do, but it seems the actual definition is slightly different to what I thought it was. – MageJohn Feb 06 '13 at 16:21
  • Yes, curses is a bit of a strange beast sometimes, and it's certainly not the best-documented API. Once you add the fact that you're indirecting through yet another API (the Python layer), you might find it hard to find reliable reference information - I would suggest writing little test apps and playing around to validate your assumptions whenever you use a new function. – Cartroo Feb 06 '13 at 16:40
1

There are two problems with this code.

First, as the previous poster noted, subwindows share a buffer with the parent window, so you should use curses.newwin() if you want a completely independent window.

Second, using del to remove a window is problematic because it relies on reference counting/garbage collection to work properly. (For one thing, you have to delete all references to the window for it to work.) I recommend using the curses.panel module to explicitly show/hide the window.

augurar
  • 12,081
  • 6
  • 50
  • 65