pythonpython-3.xterminalcurseskonsole

Python - curses.window method addstr ignores x-coordinate on some lines


I am trying to display some formatted text in python using curses. I am having a strange issue with the curses.window object, specifically with the addstr method1. I want to call this method a few times, and with each call add a string to a different line. Here is a sample:

import curses

scr = curses.initscr()
curses.reset_shell_mode()
scr.clear()
scr.addstr(30, 30, "ABCDEFG")
scr.addstr(31, 30, "HIJK")
scr.addstr(32, 30, "LMNO")
scr.addstr(33, 30, "PQRS")
scr.move(35, 5)
scr.refresh()

What I am expecting is this:

[...empty lines go here...]
                              ABCDEFG
                              HIJK
                              LMNO
                              PQRS

What I get is this:

[...empty lines go here...]
                              ABCDEFG
                              HIJK
LMNO
PQRS

Why are the last two rows not indented by the same 30 spaces as the previous rows? I was able to come up with some other examples that affected a different number of lines, but I'm not sure why it's displaying like this. How can I do this using addstr without it ignoring the x-coordinate of some lines?

If it matters, I am running Linux x86 with KDE using the Konsole terminal emulator.


1: I get similar behavior with addch, so I am guessing the solution to one will apply to the other


Solution

  • This is caused by the use of curses.reset_shell_mode. To avoid this problem, call curses.reset_prog_mode before modifying the contents of a window, and then if you need to switch back to shell mode, call curses.reset_shell_mode again. You can call these two methods repeatedly to switch back and forth as needed during the life of the program.

    Here is an example that will work correctly:

    import curses
    
    scr = curses.initscr()
    
    # should display correctly
    scr.clear()
    scr.addstr(30, 30, "ABCDEFG")
    scr.addstr(31, 30, "HIJK")
    scr.addstr(32, 30, "LMNO")
    scr.addstr(33, 30, "PQRS")
    scr.move(35, 5)
    scr.refresh()
    curses.reset_shell_mode()
    

    Note that reset_shell_mode is called at the end. If you then run this snippet, it will display incorrectly:

    # should display wrong
    scr.clear()
    scr.addstr(30, 30, "ABCDEFG")
    scr.addstr(31, 30, "HIJK")
    scr.addstr(32, 30, "LMNO")
    scr.addstr(33, 30, "PQRS")
    scr.move(35, 5)
    scr.refresh()
    curses.reset_prog_mode()
    

    And since we called reset_prog_mode at the end, running this snippet will show the correct output again:

    # should display correctly
    scr.clear()
    scr.addstr(30, 30, "ABCDEFG")
    scr.addstr(31, 30, "HIJK")
    scr.addstr(32, 30, "LMNO")
    scr.addstr(33, 30, "PQRS")
    scr.move(35, 5)
    scr.refresh()
    curses.reset_shell_mode()