curses在调用底部右下角addch时失败。

14

我开始学习Python中的curses。我正在使用Mac OS X上的Python 3.5。当我尝试在右下角写入时,程序会崩溃,并显示以下错误:

$ python ex_curses.py
[...]
  File "ex_curses.py", line 19, in do_curses
    screen.addch(mlines, mcols, 'c')
  _curses.error: add_wch() returned ERR

示例程序为:

import curses

def do_curses(screen):
    curses.noecho()
    curses.curs_set(0)
    screen.keypad(1)

    (line, col) = 12, 0
    screen.addstr(line, col, "Hello world!")
    line += 1
    screen.addstr(line, col, "Hello world!", curses.A_REVERSE)

    screen.addch(0, 0, "c")

    (mlines, mcols) = screen.getmaxyx()
    mlines -= 1
    mcols -= 1
    screen.addch(mlines, mcols, 'c')

    while True:
        event = screen.getch()
        if event == ord("q"):
            break
    curses.endwin()

if __name__ == "__main__":
    curses.wrapper(do_curses)

我有一种感觉,似乎错过了某个显而易见的东西,但我不知道那是什么。

5个回答

7

这是addch的预期行为(即怪异的行为),因为在添加字符后,它尝试换行到下一行。在lib_addch.c中有一条注释来处理这个问题:

/*
 * The _WRAPPED flag is useful only for telling an application that we've just
 * wrapped the cursor.  We don't do anything with this flag except set it when
 * wrapping, and clear it whenever we move the cursor.  If we try to wrap at
 * the lower-right corner of a window, we cannot move the cursor (since that
 * wouldn't be legal).  So we return an error (which is what SVr4 does).
 * Unlike SVr4, we can successfully add a character to the lower-right corner
 * (Solaris 2.6 does this also, however).
 */

5

给未来的读者。 在@Thomas Dickey的回答之后,我已经将以下代码片段添加到我的代码中。

try: 
    screen.addch(mlines, mcols, 'c')
except _curses.error as e:
    pass 

现在我的代码看起来像这样:
import curses
import _curses

def do_curses(screen):
    curses.noecho()
    curses.curs_set(0)
    screen.keypad(1)

    (line, col) = 12, 0
    screen.addstr(line, col, "Hello world!")
    line += 1
    screen.addstr(line, col, "Hello world!", curses.A_REVERSE)

    screen.addch(0, 0, "c")

    (mlines, mcols) = screen.getmaxyx()
    mlines -= 1
    mcols -= 1
    try:
        screen.addch(mlines, mcols, 'c')
    except _curses.error as e:
        pass

    while True:
        event = screen.getch()
        if event == ord("q"):
            break
    curses.endwin()

if __name__ == "__main__":
    curses.wrapper(do_curses)

4

window.insch(...) 可以在窗口的右下角放置一个字符而不会移动光标。该位置的任何字符都将被向右推,而不会导致错误。


1
这是正确的答案,至少在Python 3.6.8版本中是这样的。 - suprjami
我请求OP @Giovanni考虑将此答案标记为正确答案,因为它提供了一个有效的解决问题的方法。 - Anchith Acharya

0

这是针对Brian的答案的扩展。我决定选择基于写作位置(y,x)的方法。

method = window.addch if (y+1, x+1) != window.getmaxyx() else window.inschr
method(y, x, char)

由于方法addchinsch具有相同的调用签名。


0

这是一种比较奇特的解决方案,利用了模块在绘制边框时可以实际绘制右下角字符(而不会引发异常)的特性。以下是完整示例:

# -*- coding: utf-8 -*-
import curses


def addch_bottom_right(window, ch):
    """
    Somehow, the underlying ncurses library has an issue
    with writing a char into bottom-right corner
    (see the https://dev59.com/-loV5IYBdhLWcg3wVNea#36389161 for example).

    But we can use the workaround:
    - create a subwindow 1x1 of the current window in the bottom-right corner
    - draw a border of that window, consisting only of the desired character:
      for a 1x1 window, that border will consist exclusively of this single character.
    - refresh the screen to show your new 'window' with the 'border'.
    """
    print("Putting char '%s' in the bottom-right corner" % ch)
    beg_y, beg_x = window.getbegyx()
    max_y, max_x = window.getmaxyx()
    br_y = beg_y + max_y - 1
    br_x = beg_x + max_x - 1

    print('Coordinates of current window: %sx%s' % (br_y, br_x))
    w = window.subwin(1, 1, br_y, br_x)

    # only 'br' (bottom-right corner) gets printed for 1x1 box
    w.border(*([ch] * 8))
    w.noutrefresh()
    window.noutrefresh()
    curses.doupdate()


def demo(screen, show_border=True):
    """
    Try the workaround with three different windows nesting levels.

    Borders drawn here only to show where the windows are.
    """
    curses.curs_set(0)

    w = screen.subwin(8, 8, 10, 10)
    if show_border:
        w.border()
    addch_bottom_right(w, 'Window'[0])

    w2 = w.subwin(3, 3, 12, 12)
    if show_border:
        w2.box()
    addch_bottom_right(w2, 'Subwindow'[0])

    addch_bottom_right(screen, 'Main screen'[0])

    screen.getch()


if __name__ == '__main__':
    curses.wrapper(demo)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接