【问题标题】:How do I accept input from arrow keys, or accept directional input?如何接受箭头键的输入,或接受方向输入?
【发布时间】:2014-05-10 07:05:24
【问题描述】:

这可能是xy问题,但我正在尝试构建一个基于内核的文本编辑器,类似于vimnano,并且我知道如何使用转义字符来清除屏幕,然后重新打印,我可以让它接受字符,但我不知道如何让它接受用于导航的箭头输入。我认为它们有 ASCII 值,但显然没有。有没有办法使用箭头,还是我必须像vim那样制作导航模式和插入模式?

我也曾短暂地玩过curses,但这令人望而却步,因为据我所知,必须为它打开一个全新的窗口,这与我拥有的单个终端窗口的愿景不兼容.

请注意curses 不起作用,因为它清除了我不想要的窗口。

【问题讨论】:

  • 我想你可能误解了curses window 的含义。它是一个打印在终端屏幕in 中的窗口。因此,如果您打开一个终端并运行您的 curses 程序,它将在同一个终端中,就像 VIm 一样。
  • @LegoStormtroopr 我想我可能有。我正在讨论 curses 的功能,并在 python 测试中尝试它,它并没有像我之前看到的那样打开新窗口。也许我会再给“诅咒”一次机会。
  • @JFA 你是说 IDLE 吗? IDLE 不是终端,因此当您尝试使用curses 运行某些东西时,它必须打开一个终端才能使其工作。但是,如果您已经打开了一个终端,那么它将使用它。
  • 对,空闲。我只使用 IDLE 来测试代码片段,然后在我的代码中实现它们,或者尝试新的库。

标签: python text-editor


【解决方案1】:

curses 正是您想要的。事实上,我相信 vim 用 curses 实现了它的接口。

尝试将以下代码放入名为test_curses.py的文件中:

import curses

screen = curses.initscr()
screen.addstr("Hello World!!!")
screen.refresh()
screen.getch()
curses.endwin()

现在打开一个终端(不是IDLE!一个真正的终端!)并通过以下方式运行它:

python test_curses.py

您应该会看到 终端 已被清除,并且出现了 Hello World!!! 字样。按任意键,程序将停止,恢复旧的终端内容。

请注意,curses 库并不像您可能习惯的那样简单和“用户友好”。我建议阅读tutorial(不幸的是C语言,但python接口大体相同)

【讨论】:

  • 是的,我昨晚在玩诅咒,这和玩火没什么不同,但我想这就是我要找的。​​span>
  • @JFA 图书馆之所以被称为curses,那一个原因:你在使用它时通常会诅咒整个世界!它没有用户友好的 API。它也是很久以前写的,当时终端和计算机图形很慢,因此很多 API 都是在考虑优化的情况下编写的,这在你需要时很棒,但在常见情况下会使一切变得更加复杂。
  • 有没有办法创建一个安全文件来为curses 恢复我的终端?我一直在使用一个不回显或打印的终端。还有,curses是不是一定要清屏,还是输入后小段可以工作。
  • @JFA 你应该记得打电话给curses.endwin()。如果您中止应用程序(例如使用Ctrl+C),您可能最终会遇到这种情况。为避免这种情况,您可以捕获KeyboardInterrupt,调用curses.endwin(),然后重新引发异常,或者添加sys.excepthook。 AFAIK curses 仅适用于整个终端。
  • curses... 如果是这样,它会做我试图避免的事情。
【解决方案2】:

我最终使用了来自 this question 的代码,并修改了 __init__ 语句,使其在一个列表中最多接受 3 个字符。

import sys

class _Getch:
   """Gets a single character from standard input.  Does not echo to the
screen."""
   def __init__(self):
      self.impl = _GetchUnix()

   def __call__(self):# return self.impl()
      charlist = []
      counter = 0
      for i in range(3):
         try:charlist.append(self.impl())
         except:pass
         if charlist[i] not in [chr(27),chr(91)]:#TODO sort out escape vs arrow duh use len()
            break
         if len(charlist) > 1:
            if charlist == [chr(27),chr(27)]:
               break
      if len(charlist) == 3:
         if charlist[2] == 'a'
            return 'u-arr'
         if charlist[2] == 'b'
            return 'd-arr'
         if charlist[2] == 'c'
            return 'r-arr'
         if charlist[2] == 'd'
            return 'l-arr'
      if len(charlist == 2):
         if charlist == [chr(27),chr(27)]
            return chr(27)
      if len(charlist == 1)
         return charlist[0]
      return ''

class _GetchUnix:
   def __init__(self):
      import tty, sys

   def __call__(self):
      import sys, tty, termios
      fd = sys.stdin.fileno()
      old_settings = termios.tcgetattr(fd)
      try:
         tty.setraw(sys.stdin.fileno())
         ch = sys.stdin.read(1)
      finally:
         termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
      return ch

这使我可以从键盘上为编辑器获取箭头键以及所有其他字符和转义序列。它使“Getch”类不是严格意义上的 get char 克隆,因为它返回一个字符串,但它最终变得更加有用。

【讨论】:

    【解决方案3】:

    我知道我迟到了,但我真的很喜欢@elbaschid 提到的click 包。我不知道为什么他没有被投票 - 可能是因为他的示例没有显示如何专门处理光标键。

    这是我的 0.02 美元:

    #!/usr/bin/python
    
    import click
    
    printable = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
    
    while True:
        click.echo('Continue? [yn] ', nl=False)
        c = click.getchar()
        click.echo()
        if c == 'y':
            click.echo('We will go on')
        elif c == 'n':
            click.echo('Abort!')
            break
        elif c == '\x1b[D':
            click.echo('Left arrow <-')
        elif c == '\x1b[C':
            click.echo('Right arrow ->')
        else:
            click.echo('Invalid input :(')
            click.echo('You pressed: "' + ''.join([ '\\'+hex(ord(i))[1:] if i not in printable else i for i in c ]) +'"' )
    

    这会处理光标键,并作为奖励打印它尚未识别的任何键盘快捷键的 py 字符串表示。例如 Ctrl-s 是"\x13"。你以后可以在另一个里面使用它

    elif c == ??
    

    我尝试将编辑添加到@elbaschid 答案,但被拒绝了¯\_(ツ)_/¯。如果你也喜欢我的回答,请给他点赞

    用于快速命令行原型设计的很棒的库。

    【讨论】:

      【解决方案4】:

      用于构建命令行客户端的 Python 包 click 还附带一个实现,允许您获取按键事件:

      import click
      
      key = click.getchar()
      

      它将键表示形式返回为 Unicode 字符,并且“箭头键之类的东西将以平台的本机转义格式显示。”。

      这是直接取自click documentation on getchar的示例:

      import click
      
      click.echo('Continue? [yn] ', nl=False)
      c = click.getchar()
      click.echo()
      if c == 'y':
          click.echo('We will go on')
      elif c == 'n':
          click.echo('Abort!')
      else:
          click.echo('Invalid input :(')
      

      【讨论】:

      • 这个实际上非常不错的包和非常不错的答案 - 除了它缺乏处理箭头键 - 我已经让自己解决了这个小问题
      【解决方案5】:

      在按下箭头键或任何其他键时执行所需的操作

      # key_event_handler.py
      
      import sys
      import select
      import pty
      import os
      import time
      import fcntl
      import tty
      import termios
      def __select( iwtd, owtd, ewtd, timeout=None):
      
         '''This is a wrapper around select.select() that ignores signals. If
         select.select raises a select.error exception and errno is an EINTR
         error then it is ignored. Mainly this is used to ignore sigwinch
         (terminal resize). '''
      
         # if select() is interrupted by a signal (errno==EINTR) then
         # we loop back and enter the select() again.
         if timeout is not None:
             end_time = time.time() + timeout
         while True:
             try:
                 return select.select(iwtd, owtd, ewtd, timeout)
             except select.error:
                 err = sys.exc_info()[1]
                 if err.args[0] == errno.EINTR:
                     # if we loop back we have to subtract the
                     # amount of time we already waited.
                     if timeout is not None:
                         timeout = end_time - time.time()
                         if timeout < 0:
                             return([], [], [])
                 else:
                     # something else caused the select.error, so
                     # this actually is an exception.
                     raise
      
      STDIN_FILENO=pty.STDIN_FILENO
      STDOUT_FILENO=pty.STDOUT_FILENO
      string_type=bytes
      sys.stdout.write(string_type())
      sys.stdout.flush()
      buffer = string_type()
      mode = tty.tcgetattr(STDIN_FILENO)
      tty.setraw(STDIN_FILENO)
      try:
          while True:
              r, w, e = __select([STDIN_FILENO], [], [],timeout=1)
              if STDIN_FILENO in r:
                  #It accepts all keys from keyboard 
                  data=os.read(STDIN_FILENO, 1)
                  #Bellow line returns ASCII value of a charector
                  ascii_value=ord(data[0])
                  ##########################################################################
                  ##                      Your code goes here                             ## 
                  ##                                                                      ##
                  # Do some action here by matching the ASCII value                        #
                  # you can handle your program by making use of special keys like         #
                  # Backspace, Ctrl, Ctrl+A,Ctrl+B, Ctrl+C, ...Ctrl+Z, Esc,F1, ...,F12 ....#
                  # Tab,Enter,Arrow keys,Alphabetic and Numeric keys are also supported    #  
                  ##########################################################################
                  #                                                                        #
                  #To Print use bellow line rather than print or sys.stdout.write(data)    #
                  #os.write(STDOUT_FILENO,data)                                            #
                  ##                                                                       #
                  ##########################################################################
      
      
      finally:
          tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode) 
      

      然后打开终端运行key_event_handler.py


      这个程序主要是捕获按键并获取按键的ascii,这个程序也可以用于多线程应用程序中的非阻塞I/O

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-09-10
        • 2021-08-21
        • 1970-01-01
        • 2012-09-25
        • 1970-01-01
        • 2011-05-21
        • 2014-05-04
        • 2021-09-14
        相关资源
        最近更新 更多