Python curses - 在输入文本时调整终端大小

时间:2017-11-12 08:08:25

标签: python ncurses curses

我正在尝试使用curses进行简单的GUI,但在调整大小和重新绘制屏幕时我遇到了一些问题。 这是我的代码:

import curses
import curses.textpad as textpad

class editor (object):
    def __init__(self, stdscreen):
        self.screen = stdscreen
        self.height,self.width = self.screen.getmaxyx()
        self.draw_screen()
        while True:
            # Check if the screen has been resized
            resize = curses.is_term_resized(self.height, self.width)
            if resize:
                self.height,self.width = self.screen.getmaxyx()
                self.draw_screen()
            curses.noecho()
            text = textpad.Textbox(self.text).edit(validate=self.validate)
            self.body.addstr(0,0, str(text), curses.A_NORMAL)
            self.body.refresh()
            self.text.erase()
    def draw_screen (self):
        self.screen.clear()
        self.header=self.screen.subwin(1,self.width,0,0)
        self.body=self.screen.subwin(self.height-3,self.width,1,0)
        self.text=self.screen.subwin(1,self.width,self.height-2,0)
        self.footer=self.screen.subwin(self.height-1,0)
        header_text = "{0:<{n}}".format("Header", n=self.width-1)
        self.header.addstr(0,0, header_text, curses.A_REVERSE)
        self.footer.addstr(0,0, "^W", curses.A_REVERSE)
        self.footer.addstr(0,2, " Quit", curses.A_NORMAL)
        self.header.noutrefresh()
        self.body.noutrefresh()
        self.footer.noutrefresh()
        self.text.noutrefresh()
        curses.doupdate()
    def validate (self, ch):
        # If resize event redraw screen
        if ch==curses.KEY_RESIZE:
            self.height,self.width = self.screen.getmaxyx()
            self.draw_screen()
        if ch==23: # ^w quit
            exit()
        else:
            return ch

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

当终端没有调整大小时,它的工作方式与预期的一样,但是当我放大终端窗口时,当页眉和页脚分别移动到顶行和底行时,光标保持在前一个位置,直到字符串被接受为止按Enter键。 收缩时相同,除了在这种情况下光标越过页脚并返回页脚的内容。 试图调试问题我发现子窗口似乎移动得很好,但光标不随它们移动。我也试过了

self.text.move(0,0)

将光标移回正确的位置,但它没有工作。

加成: 如何在调整大小操作之前保留插入的文本并将其添加到已调整大小的显示屏幕?

修改 经过一些调试后,我想出了一个伪解决方案,其中包含了#34;工作(但丑陋和hacky)。

import curses
import curses.textpad as textpad

class my_textbox(textpad.Textbox):

    def edit(self, validate=None):
        while 1:
            ch = self.win.getch()
            # Return a list instead of a string if the last key was a resize
            if ch==curses.KEY_RESIZE:
                return[self.gather(),True]
            if validate:
                ch = validate(ch)
            if not ch:
                continue
            if not self.do_command(ch):
                break
            self.win.refresh()
        return self.gather()

class editor (object):
    def __init__(self, stdscreen):
        self.screen = stdscreen
        self.height,self.width = self.screen.getmaxyx()
        # Initialize a variable to store the previous text
        self.ptext=None
        curses.noecho()
        self.draw_screen()
        while True:
            # Check if the screen has been resized
            resize = curses.is_term_resized(self.height, self.width)
            if resize:
                self.height,self.width = self.screen.getmaxyx()
                self.draw_screen()
            text = my_textbox(self.text).edit(validate=self.validate)
            # If the window has been resized (aka textbox returned a list)
            if isinstance (text, list):
                text=text[0]
                # Store the previous text
                self.ptext=text.strip()
                continue
            self.body.addstr(0,0, str(text), curses.A_NORMAL)
            self.body.refresh()
            self.text.erase()
    def draw_screen (self):
        self.screen.clear()
        self.header=self.screen.subwin(1,self.width,0,0)
        self.body=self.screen.subwin(self.height-3,self.width,1,0)
        self.text=self.screen.subwin(1,self.width,self.height-2,0)
        self.footer=self.screen.subwin(self.height-1,0)
        header_text = "{0:<{n}}".format("Header", n=self.width-1)
        self.header.addstr(0,0, header_text, curses.A_REVERSE)
        # If there was text previous to resize
        if self.ptext:
            # To prevent _curses.error: addwstr() returned ERR
            # when shrinking window cut out the last character
            # (!) Raises ERR anyway when shrinking with double click from
            # titlebar, also, when resizing is too fast, ptext become a series of ^?
            if len(self.ptext)<self.width-1:
                self.text.addstr(0,0,self.ptext.strip())
            else:
                self.ptext=self.ptext[:self.width-1]
                self.text.addstr(0,0,self.ptext.strip())
        self.footer.addstr(0,0, "^W", curses.A_REVERSE)
        self.footer.addstr(0,2, " Quit", curses.A_NORMAL)
        self.header.noutrefresh()
        self.body.noutrefresh()
        self.footer.noutrefresh()
        self.text.noutrefresh()
        curses.doupdate()

    def validate (self, ch):
        if ch==23: # ^w quit
            exit()
        else:
            return ch

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

我做过的事情

我重新定义了Textbox类的 edit 方法,这样当调整窗口大小时,它会返回一个列表而不是字符串。

在调整窗口大小时的输入循环中,前一个文本被存储,并开始一个新的循环。

如果存在上一个文本,则绘图功能会将其添加到子窗口。

注释:

双击标题栏时调整大小会引发错误,如果调整大小过快&#34;字符串被垃圾替换(&#34; ^?&#34;,在被curses转换之前实际上是2 ^ 64-1)。

我认为因为(n)curses的大多数功能都是用不可调整大小的终端创建的,所以实现确实有意义,但至少对于像我这样相对较新的python的人来说,根本不直观

1 个答案:

答案 0 :(得分:1)

说明听起来很正确:您的应用程序必须重新布局文本并重新绘制屏幕内容。

为此,它必须保留信息记录。其中一些是预定义的,例如此行中的"Header"

header_text = "{0:<{n}}".format("Header", n=self.width-1)

但是如果屏幕变小, sub 窗口的内容可能会丢失(因为ncurses中的resizeterm会截断这些窗口):

self.header=self.screen.subwin(1,self.width,0,0)

以及任何窗口,例如,使用newwin创建的窗口。 Python的textpad类就是这样做的。

curses(特别是Python使用的ncurses)支持一个名为"pad"的准窗口,您可以在某些情况下使用它。但它的 textpad 类并不容易使用(因为刷新 pad 的基础调用与 window 的基础调用不同)。

但是(这需要一些实际工作),你可以创建一个 pad ,它与你想要的 screen 一样大管理,并使 textpad 成为其子项,然后独立于resizeterm进行自己的重新布局。 Python相关resize_term笔记的文档(从ncurses manual复制而来):

  

resizeterm() 使用的后端功能,执行大部分工作;调整窗口大小时, resize_term() 会填充扩展区域。调用应用程序应使用适当的数据填写这些区域。 resize_term() 函数会尝试调整所有窗口的大小。但是,由于 pad 的调用约定,如果不与应用程序进行额外交互,则无法调整这些大小。