"""Simple textbox editing widget with Emacs-like keybindings."""import cursesimport curses.asciidef rectangle(win, uly, ulx, lry, lrx):"""Draw a rectangle with corners at the provided upper-leftand lower-right coordinates."""win.vline(uly+1, ulx, curses.ACS_VLINE, lry - uly - 1)win.hline(uly, ulx+1, curses.ACS_HLINE, lrx - ulx - 1)win.hline(lry, ulx+1, curses.ACS_HLINE, lrx - ulx - 1)win.vline(uly+1, lrx, curses.ACS_VLINE, lry - uly - 1)win.addch(uly, ulx, curses.ACS_ULCORNER)win.addch(uly, lrx, curses.ACS_URCORNER)win.addch(lry, lrx, curses.ACS_LRCORNER)win.addch(lry, ulx, curses.ACS_LLCORNER)class Textbox:"""Editing widget using the interior of a window object.Supports the following Emacs-like key bindings:Ctrl-A Go to left edge of window.Ctrl-B Cursor left, wrapping to previous line if appropriate.Ctrl-D Delete character under cursor.Ctrl-E Go to right edge (stripspaces off) or end of line (stripspaces on).Ctrl-F Cursor right, wrapping to next line when appropriate.Ctrl-G Terminate, returning the window contents.Ctrl-H Delete character backward.Ctrl-J Terminate if the window is 1 line, otherwise insert newline.Ctrl-K If line is blank, delete it, otherwise clear to end of line.Ctrl-L Refresh screen.Ctrl-N Cursor down; move down one line.Ctrl-O Insert a blank line at cursor location.Ctrl-P Cursor up; move up one line.Move operations do nothing if the cursor is at an edge where the movementis not possible. The following synonyms are supported where possible:KEY_LEFT = Ctrl-B, KEY_RIGHT = Ctrl-F, KEY_UP = Ctrl-P, KEY_DOWN = Ctrl-NKEY_BACKSPACE = Ctrl-h"""def __init__(self, win, insert_mode=False):self.win = winself.insert_mode = insert_modeself._update_max_yx()self.stripspaces = 1self.lastcmd = Nonewin.keypad(1)def _update_max_yx(self):maxy, maxx = self.win.getmaxyx()self.maxy = maxy - 1self.maxx = maxx - 1def _end_of_line(self, y):"""Go to the location of the first blank on the given line,returning the index of the last non-blank character."""self._update_max_yx()last = self.maxxwhile True:if curses.ascii.ascii(self.win.inch(y, last)) != curses.ascii.SP:last = min(self.maxx, last+1)breakelif last == 0:breaklast = last - 1return lastdef _insert_printable_char(self, ch):self._update_max_yx()(y, x) = self.win.getyx()backyx = Nonewhile y < self.maxy or x < self.maxx:if self.insert_mode:oldch = self.win.inch()# The try-catch ignores the error we trigger from some curses# versions by trying to write into the lowest-rightmost spot# in the window.try:self.win.addch(ch)except curses.error:passif not self.insert_mode or not curses.ascii.isprint(oldch):breakch = oldch(y, x) = self.win.getyx()# Remember where to put the cursor back since we are in insert_modeif backyx is None:backyx = y, xif backyx is not None:self.win.move(*backyx)def do_command(self, ch):"Process a single editing command."self._update_max_yx()(y, x) = self.win.getyx()self.lastcmd = chif curses.ascii.isprint(ch):if y < self.maxy or x < self.maxx:self._insert_printable_char(ch)elif ch == curses.ascii.SOH: # ^aself.win.move(y, 0)elif ch in (curses.ascii.STX,curses.KEY_LEFT, curses.ascii.BS,curses.KEY_BACKSPACE):if x > 0:self.win.move(y, x-1)elif y == 0:passelif self.stripspaces:self.win.move(y-1, self._end_of_line(y-1))else:self.win.move(y-1, self.maxx)if ch in (curses.ascii.BS, curses.KEY_BACKSPACE):self.win.delch()elif ch == curses.ascii.EOT: # ^dself.win.delch()elif ch == curses.ascii.ENQ: # ^eif self.stripspaces:self.win.move(y, self._end_of_line(y))else:self.win.move(y, self.maxx)elif ch in (curses.ascii.ACK, curses.KEY_RIGHT): # ^fif x < self.maxx:self.win.move(y, x+1)elif y == self.maxy:passelse:self.win.move(y+1, 0)elif ch == curses.ascii.BEL: # ^greturn 0elif ch == curses.ascii.NL: # ^jif self.maxy == 0:return 0elif y < self.maxy:self.win.move(y+1, 0)elif ch == curses.ascii.VT: # ^kif x == 0 and self._end_of_line(y) == 0:self.win.deleteln()else:# first undo the effect of self._end_of_lineself.win.move(y, x)self.win.clrtoeol()elif ch == curses.ascii.FF: # ^lself.win.refresh()elif ch in (curses.ascii.SO, curses.KEY_DOWN): # ^nif y < self.maxy:self.win.move(y+1, x)if x > self._end_of_line(y+1):self.win.move(y+1, self._end_of_line(y+1))elif ch == curses.ascii.SI: # ^oself.win.insertln()elif ch in (curses.ascii.DLE, curses.KEY_UP): # ^pif y > 0:self.win.move(y-1, x)if x > self._end_of_line(y-1):self.win.move(y-1, self._end_of_line(y-1))return 1def gather(self):"Collect and return the contents of the window."result = ""self._update_max_yx()for y in range(self.maxy+1):self.win.move(y, 0)stop = self._end_of_line(y)if stop == 0 and self.stripspaces:continuefor x in range(self.maxx+1):if self.stripspaces and x > stop:breakresult = result + chr(curses.ascii.ascii(self.win.inch(y, x)))if self.maxy > 0:result = result + "\n"return resultdef edit(self, validate=None):"Edit in the widget window and collect the results."while 1:ch = self.win.getch()if validate:ch = validate(ch)if not ch:continueif not self.do_command(ch):breakself.win.refresh()return self.gather()if __name__ == '__main__':def test_editbox(stdscr):ncols, nlines = 9, 4uly, ulx = 15, 20stdscr.addstr(uly-2, ulx, "Use Ctrl-G to end editing.")win = curses.newwin(nlines, ncols, uly, ulx)rectangle(stdscr, uly-1, ulx-1, uly + nlines, ulx + ncols)stdscr.refresh()return Textbox(win).edit()str = curses.wrapper(test_editbox)print('Contents of text box:', repr(str))
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。