【发布时间】:2018-04-25 02:20:28
【问题描述】:
我正在尝试使用 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)
将光标移回正确的位置,但是没有用。
奖励: 如何保留在调整大小操作之前插入的文本并将其添加到调整大小的显示屏幕?
编辑: 经过更多调试后,我想出了一个“有点”可行的伪解决方案(但丑陋和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 方法,以便在调整窗口大小时返回一个列表而不是字符串。
在调整窗口大小时的输入循环中,先前的文本被存储并开始一个新的循环。
如果之前的文本存在,绘图函数会将其添加到子窗口中。
注释:
通过双击标题栏来调整大小会引发错误,如果调整大小“太快”,则字符串将被垃圾替换(“^?”,实际上是 2^64-1 在被诅咒转换之前)。
我想因为 (n)curses 的大部分功能都是在考虑不可调整大小的终端创建的,所以实现确实有意义,但至少对于像我这样对 python 相对较新的人来说,根本不直观.
【问题讨论】: