如何检查是不是已在 Ruby 和 Windows 控制台中按下箭头键

Posted

技术标签:

【中文标题】如何检查是不是已在 Ruby 和 Windows 控制台中按下箭头键【英文标题】:How can I check if the arrow keys have been pressed in Ruby and Windows Console如何检查是否已在 Ruby 和 Windows 控制台中按下箭头键 【发布时间】:2015-03-22 20:33:55 【问题描述】:

我正在使用 Ruby 2.1.5 和 Windows 控制台。我需要能够在不停止执行的情况下扫描按键。我需要能够专门检测箭头键。我在其他问题上尝试了许多推荐的方法,但它们都不能在 Windows 控制台上工作或检测不到箭头键。

不显示箭头键

require 'io/console'
loop do
  p STDIN.getch
end

运行没有错误,但似乎没有从输入缓冲区读取,因为在使用 Ctrl-C 退出程序后,所有按下的键都显示在控制台上

require 'io/wait'

def char_if_pressed
  begin

    system("stty raw -echo") # turn raw input on
    c = nil
    if $stdin.ready?
      c = $stdin.getc
    end
    c.chr if c
  ensure
      system "stty -raw echo" # turn raw input off
  end
end

while true
  c = char_if_pressed
  puts "[#c]" if c
  sleep 1
  puts "tick"
end

这个也可以正常运行,但需要按下回车键才能继续运行。我需要非阻塞输入

# read a character without pressing enter and without printing to the screen
def read_char
  begin
    # save previous state of stty
    old_state = "stty raw -g"
    # disable echoing and enable raw (not having to press enter)
    system "stty raw -echo"
    c = STDIN.getc.chr
    # gather next two characters of special keys
    if(c=="\e")
      puts "checking for non alpha"
      extra_thread = Thread.new
        c = c + STDIN.getc.chr
        c = c + STDIN.getc.chr
      
      # wait just long enough for special keys to get swallowed
      extra_thread.join(0.00001)
      # kill thread so not-so-long special keys don't wait on getc
      extra_thread.kill
    end
  rescue => ex
    puts "#ex.class: #ex.message"
    puts ex.backtrace
  ensure
    # restore previous state of stty
    system "stty #old_state"
  end
  return c
end

# takes a single character command
def show_single_key
  c = read_char
  case c
    when " "
      puts "SPACE"
    when "\t"
      puts "TAB"
    when "\r"
      puts "RETURN"
    when "\n"
      puts "LINE FEED"
    when "\e"
      puts "ESCAPE"
    when "\e[A"
      puts "UP ARROW"
    when "\e[B"
      puts "DOWN ARROW"
    when "\e[C"
      puts "RIGHT ARROW"
    when "\e[D"
      puts "LEFT ARROW"
    when "\177"
      puts "BACKSPACE"
    when "\004"
      puts "DELETE"
    when /^.$/
      puts "SINGLE CHAR HIT: #c.inspect"
    else
      puts "SOMETHING ELSE: #c.inspect"
  end
end
show_single_key while(true)

这似乎是最后一个的变体,它适用于所有 ASCII 表示的键,但不适用于箭头键,并且输入有问题。

require 'io/console'

# Reads keypresses from the user including 2 and 3 escape character sequences.
def read_char
  STDIN.echo = false
  STDIN.raw!

  input = STDIN.getc.chr
  puts input
  if input == "\e" then
    puts "getting additional characters"
    input << STDIN.read_nonblock(3) rescue nil
    input << STDIN.read_nonblock(2) rescue nil
  end
ensure
  #STDIN.echo = true
  STDIN.cooked!

  return input
end

# oringal case statement from:
# http://www.alecjacobson.com/weblog/?p=75
def show_single_key
  c = read_char

  case c
  when " "
    puts "SPACE"
  when "\t"
    puts "TAB"
  when "\r"
    puts "RETURN"
  when "\n"
    puts "LINE FEED"
  when "\e"
    puts "ESCAPE"
  when "\e[A"
    puts "UP ARROW"
  when "\e[B"
    puts "DOWN ARROW"
  when "\e[C"
    puts "RIGHT ARROW"
  when "\e[D"
    puts "LEFT ARROW"
  when "\177"
    puts "BACKSPACE"
  when "\004"
    puts "DELETE"
  when "\e[3~"
    puts "ALTERNATE DELETE"
  when "\u0003"
    puts "CONTROL-C"
    exit 0
  when /^.$/
    puts "SINGLE CHAR HIT: #c.inspect"
  else
    puts "SOMETHING ELSE: #c.inspect"
  end
end

show_single_key while(true)

我还尝试了几个调用 Win32API 的方法,但它们都无法正常工作,并且有关于被弃用的警告。我想要一个跨平台的解决方案,但此时我会满足于它在 Windows 控制台上正常工作。我也不能为此使用任何 Ruby Gems。

【问题讨论】:

以前没有 Ruby 专家遇到过这种情况? 为什么不能使用宝石?这似乎是一个非常严格、任意的条件。正确的方法是使用 curses 库。有很多 gem 为此类库提供包装器,但我想您将不得不自己编写这样的包装器。从头开始。无缘无故。 有时您必须使用限制性的任意条件。我将无法访问将运行此代码的机器,也无法在其上安装任何 Ruby Gem。如果有一种方法可以包含 Gem 的功能并将它们与我的代码一起发送,而无需在另一台机器上安装 Gem,我会很乐意使用它们。我对 Ruby 有点陌生,但不是必须在运行程序的每台计算机上都有 gem,或者有没有办法将你的 ruby​​ 代码“编译”到它将在每台具有 ruby​​ 解释器的机器上运行的地方他们有什么宝石? Gem 只是 Ruby 代码的集合,供您发送至require。即使您无法使用包管理器正确安装 gem,也没有什么能阻止您手动下载 gem 并将其文件包含在您的脚本中。 诅咒我该怎么做? 【参考方案1】:

我一直在尝试解决这个问题一段时间,这对我有用:

require 'io/console'
while true
    user_input = $stdin.getch         
    case user_input
    when 'q'
        exit
    when '['        
        print '' # I believe that by entering this block of code user_input drops the first char. What is left can be used to determine what key has been pressed.  
    else
        case user_input  
        when 'A'
            puts "up"
        when 'B'
            puts "down"
        when 'C'
            puts "right"
        when 'D'
            puts "left"
        end
    end
end

我正在使用 ruby​​ 2.4.1。希望能帮助到你。

【讨论】:

以上是关于如何检查是不是已在 Ruby 和 Windows 控制台中按下箭头键的主要内容,如果未能解决你的问题,请参考以下文章

如何持续检查 AVAudioplayer 是不是已在 Swift 中播放完毕?

如何检查 WPF 应用程序是不是已在运行? [复制]

如何检查视图控制器是不是已在 Swift 中关闭

如何检查用户 JID 是不是已在 XMPPFrameWork 中使用

如何检查重用标识符是不是已在 UITableView 中注册?

如何在调用之前检查 Java Web 服务是不是已在 PL/SQL 中启动并运行