0

This is a small python program using curses:

import curses
import locale

def init_curses():
    stdscr = curses.initscr()
    curses.start_color()
    curses.use_default_colors()
    curses.noecho()
    curses.cbreak()
    stdscr.keypad(True)
    curses.curs_set(0)
    stdscr.encoding = 'utf-8'
    locale.setlocale(locale.LC_ALL, '')
    return stdscr

# Clean up and close the curses environment
def close_curses(stdscr):
    stdscr.keypad(False)
    curses.echo()
    curses.nocbreak()
    curses.endwin()

def main(stdscr):
    stdscr.clear()
    stdscr.addstr(1, 0, "Hello World!")
    stdscr.addstr(2, 0, "あ")
    stdscr.addstr(3, 0, "ああ")
    stdscr.addstr(4, 0, "あああ")
    stdscr.addstr(5, 0, "ああああ")
    stdscr.addstr(6, 0, "あああああ")
    stdscr.addstr(7, 0, "ああああああ")
    stdscr.addstr(8, 0, "あああああああ")
    stdscr.addstr(9, 0, "ああああああああ")
    stdscr.addstr(10, 0, "あああああああああ")

    stdscr.refresh()
    stdscr.getkey()


    
if __name__ == "__main__":
    stdscr = init_curses()
    try:
        curses.wrapper(main)
    finally:
        close_curses(stdscr)

The expected output should be this:

Hello World!
あ
ああ
あああ
ああああ
あああああ
ああああああ
あああああああ
ああああああああ
あああああああああ

Instead I get this:

Output

It's driving me insane. I'd like to know how to get the expected output. I've also run into issues where the displaced lines have characters missing on their left side sometimes, leaving me with half eaten displaced sentences.

I'm using python 3.10.2, and the windows-curses library on the windows terminal with powershell 7, if that is of any value.

1 Answers1

0

Don't know if it's still relevant to you, but maybe will be for others. By working on a similair project myself I've came to some conclusions.

One short answer that hopefully is true (just a theory from what I've tested) is that curses displays japanese characters as one-column wide, even though they should be displayed as two-column wide.

For the observation itself, I specifically wanted to write hiragana characters one after another and not only did it cut off like in your example, the cursor itself was also off. Putting a no-width special character (0x200b) was no help, but displacing the cursor itself fixed it partially - cursor was in the proper position with code close to this:

stdscr.addstr(0, 0, text)
stdscr.move(0, len(text) * 2)

That was addressing the wrong width issue, but the cut-off was still happening. Moving the refresh method directly under the clear method has fixed it though! Every tutorial I've seen always put blocks of code in specific way:

  1. clear method
  2. your block of code
  3. refresh method
  4. any getch, getkey, or terminating the app

I've put the refresh method above my block of code and the cut-off was gone.

If that is enough for you then that's that, but those I've listed here are just hacks I discovered. I have no idea if putting refresh above your code won't break anything else nor if displacing the cursor like I did is considered a good work-around.

Maybe curses has some method to set characters' width to be twice as wide, or maybe that's not even the issue entirely. I believe it is thanks to another question here. The thing stays as it is though that this is not a perfect fix, more of a work-around, but seeing how little talk there is around this topic for actual resolution, this might be the best we can muster for now.

Edit 1: If you want to work with many windows or pads then documentation states that performance may improve when separating refresh method into two. After quick check I noticed that my fix still works.

stdscr.clear()
stdscr.noutrefresh()  # Place of previous refresh

...  # Do any drawing here

curses.doupdate()  # Place where you would place refresh based on tutorials
key = stdscr.getch()

...  # Any other logic can go here