”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 如何检测从终端运行的 Python 脚本中的键盘输入?

如何检测从终端运行的 Python 脚本中的键盘输入?

发布于2024-12-21
浏览:502

How do I detect keyboard input in a Python script running from the terminal?

如何从终端检测脚本中的键盘输入?

同步/阻塞按键捕获:

简单输入或 raw_input,阻塞函数,一旦用户按下换行符,就会返回用户输入的文本。

typedString = raw_input()

一个简单的阻塞函数,等待用户按下单个键,然后返回该键

class _Getch:
 """Gets a single character from standard input.  Does not echo to the
screen. From http://code.activestate.com/recipes/134892/"""
def __init__(self):
  try:
    self.impl = _GetchWindows()
  except ImportError:
    try:
      self.impl = _GetchMacCarbon()
    except(AttributeError, ImportError):
      self.impl = _GetchUnix()

def __call__(self): return self.impl()


class _GetchUnix:
def __init__(self):
  import tty, sys, termios # import termios now or else you'll get the Unix version on the Mac

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

class _GetchWindows:
def __init__(self):
  import msvcrt

def __call__(self):
  import msvcrt
  return msvcrt.getch()

class _GetchMacCarbon:
"""
A function which returns the current ASCII key that is down;
if no ASCII key is down, the null string is returned.  The
page http://www.mactech.com/macintosh-c/chap02-1.html was
very helpful in figuring out how to do this.
"""
def __init__(self):
  import Carbon
  Carbon.Evt #see if it has this (in Unix, it doesn't)

def __call__(self):
  import Carbon
  if Carbon.Evt.EventAvail(0x0008)[0]==0: # 0x0008 is the keyDownMask
    return ''
  else:
    #
    # The event contains the following info:
    # (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
    #
    # The message (msg) contains the ASCII char which is
    # extracted with the 0x000000FF charCodeMask; this
    # number is converted to an ASCII character with chr() and
    # returned
    #
    (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
    return chr(msg & 0x000000FF)


def getKey():
  inkey = _Getch()
  import sys
  for i in xrange(sys.maxint):
    k=inkey()
    if k<>'':break

  return k

异步按键捕获:

每当用户在命令提示符中键入按键时,即使在解释器(键盘记录器)中键入内容,也会通过按下的按键调用回调

回调尚未实现。

用户按 Enter 键后使用键入的文本调用的回调(a较少的实时键盘记录器)

回调尚未实现。

程序运行时(例如,在 for 循环中)按下按键时调用的回调或 while 循环)

Windows:

import threading
from win32api import STD_INPUT_HANDLE
from win32console import GetStdHandle, KEY_EVENT, ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT


class KeyAsyncReader():
  def __init__(self):
    self.stopLock = threading.Lock()
    self.stopped = True
    self.capturedChars = ""

    self.readHandle = GetStdHandle(STD_INPUT_HANDLE)
    self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)



  def startReading(self, readCallback):
    self.stopLock.acquire()

    try:
      if not self.stopped:
        raise Exception("Capture is already going")

      self.stopped = False
      self.readCallback = readCallback

      backgroundCaptureThread = threading.Thread(target=self.backgroundThreadReading)
      backgroundCaptureThread.daemon = True
      backgroundCaptureThread.start()
    except:
      self.stopLock.release()
      raise

    self.stopLock.release()


  def backgroundThreadReading(self):
    curEventLength = 0
    curKeysLength = 0
    while True:
      eventsPeek = self.readHandle.PeekConsoleInput(10000)

      self.stopLock.acquire()
      if self.stopped:
        self.stopLock.release()
        return
      self.stopLock.release()


      if len(eventsPeek) == 0:
        continue

      if not len(eventsPeek) == curEventLength:
        if self.getCharsFromEvents(eventsPeek[curEventLength:]):
          self.stopLock.acquire()
          self.stopped = True
          self.stopLock.release()
          break

        curEventLength = len(eventsPeek)



  def getCharsFromEvents(self, eventsPeek):
    callbackReturnedTrue = False
    for curEvent in eventsPeek:
      if curEvent.EventType == KEY_EVENT:
              if ord(curEvent.Char) == 0 or not curEvent.KeyDown:
                pass
              else:
                curChar = str(curEvent.Char)
                if self.readCallback(curChar) == True:
                  callbackReturnedTrue = True


    return callbackReturnedTrue

  def stopReading(self):
    self.stopLock.acquire()
    self.stopped = True
    self.stopLock.release()

轮询:

用户只是希望能够在按下某个键时执行某些操作,而不必等待该键(因此这应该是非-阻塞)。因此,他们调用 poll() 函数,该函数要么返回一个键,要么返回 None。这可以是有损的(如果它们在轮询之间花费太长时间,它们可能会错过一个键)或非有损的(轮询器将存储所有按下的键的历史记录,因此当 poll() 函数请求它们时,它们将始终被返回按按下的顺序)。

Windows 和 OS X(也许还有 Linux):

global isWindows

isWindows = False
try:
  from win32api import STD_INPUT_HANDLE
  from win32console import GetStdHandle, KEY_EVENT, ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT
  isWindows = True
except ImportError as e:
  import sys
  import select
  import termios


class KeyPoller():
  def __enter__(self):
    global isWindows
    if isWindows:
      self.readHandle = GetStdHandle(STD_INPUT_HANDLE)
      self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)
      
      self.curEventLength = 0
      self.curKeysLength = 0
      
      self.capturedChars = []
    else:
      # Save the terminal settings
      self.fd = sys.stdin.fileno()
      self.new_term = termios.tcgetattr(self.fd)
      self.old_term = termios.tcgetattr(self.fd)
      
      # New terminal setting unbuffered
      self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
      termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)
      
    return self
  
  def __exit__(self, type, value, traceback):
    if isWindows:
      pass
    else:
      termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term)
  
  def poll(self):
    if isWindows:
      if not len(self.capturedChars) == 0:
        return self.capturedChars.pop(0)

      eventsPeek = self.readHandle.PeekConsoleInput(10000)
最新教程 更多>
  • 如何在Java字符串中有效替换多个子字符串?
    如何在Java字符串中有效替换多个子字符串?
    在java 中有效地替换多个substring,需要在需要替换一个字符串中的多个substring的情况下,很容易求助于重复应用字符串的刺激力量。 However, this can be inefficient for large strings or when working with nu...
    编程 发布于2025-05-20
  • Java为何无法创建泛型数组?
    Java为何无法创建泛型数组?
    通用阵列创建错误 arrayList [2]; JAVA报告了“通用数组创建”错误。为什么不允许这样做?答案:Create an Auxiliary Class:public static ArrayList<myObject>[] a = new ArrayList<myO...
    编程 发布于2025-05-20
  • 如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
    如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
    为有效的slug生成首先,该函数用指定的分隔符替换所有非字母或数字字符。此步骤可确保slug遵守URL惯例。随后,它采用ICONV函数将文本简化为us-ascii兼容格式,从而允许更广泛的字符集合兼容性。接下来,该函数使用正则表达式删除了不需要的字符,例如特殊字符和空格。此步骤可确保slug仅包含...
    编程 发布于2025-05-20
  • C++20 Consteval函数中模板参数能否依赖于函数参数?
    C++20 Consteval函数中模板参数能否依赖于函数参数?
    [ consteval函数和模板参数依赖于函数参数在C 17中,模板参数不能依赖一个函数参数,因为编译器仍然需要对非contexexpr futcoriations contim at contexpr function进行评估。 compile time。 C 20引入恒定函数,必须在编译时进行...
    编程 发布于2025-05-20
  • Java中假唤醒真的会发生吗?
    Java中假唤醒真的会发生吗?
    在Java中的浪费唤醒:真实性或神话?在Java同步中伪装唤醒的概念已经是讨论的主题。尽管存在这种行为的潜力,但问题仍然存在:它们实际上是在实践中发生的吗? Linux的唤醒机制根据Wikipedia关于伪造唤醒的文章,linux实现了pthread_cond_wait()功能的Linux实现,利用...
    编程 发布于2025-05-20
  • 在UTF8 MySQL表中正确将Latin1字符转换为UTF8的方法
    在UTF8 MySQL表中正确将Latin1字符转换为UTF8的方法
    在UTF8表中将latin1字符转换为utf8 ,您遇到了一个问题,其中含义的字符(例如,“jáuòiñe”)在utf8 table tabled tablesset中被extect(例如,“致电。为了解决此问题,您正在尝试使用“ mb_convert_encoding”和“ iconv”转换受...
    编程 发布于2025-05-20
  • CSS强类型语言解析
    CSS强类型语言解析
    您可以通过其强度或弱输入的方式对编程语言进行分类的方式之一。在这里,“键入”意味着是否在编译时已知变量。一个例子是一个场景,将整数(1)添加到包含整数(“ 1”)的字符串: result = 1 "1";包含整数的字符串可能是由带有许多运动部件的复杂逻辑套件无意间生成的。它也可以是故意从单个真理...
    编程 发布于2025-05-20
  • PHP阵列键值异常:了解07和08的好奇情况
    PHP阵列键值异常:了解07和08的好奇情况
    PHP数组键值问题,使用07&08 在给定数月的数组中,键值07和08呈现令人困惑的行为时,就会出现一个不寻常的问题。运行print_r($月份)返回意外结果:键“ 07”丢失,而键“ 08”分配给了9月的值。此问题源于PHP对领先零的解释。当一个数字带有0(例如07或08)的前缀时,PHP将...
    编程 发布于2025-05-20
  • 在GO中构造SQL查询时,如何安全地加入文本和值?
    在GO中构造SQL查询时,如何安全地加入文本和值?
    在go中构造文本sql查询时,在go sql queries 中,在使用conting and contement和contement consem per时,尤其是在使用integer per当per当per时,per per per当per. [&​​&&&&&&&&&&&&&&&默元组方法在...
    编程 发布于2025-05-20
  • Go语言垃圾回收如何处理切片内存?
    Go语言垃圾回收如何处理切片内存?
    Garbage Collection in Go Slices: A Detailed AnalysisIn Go, a slice is a dynamic array that references an underlying array.使用切片时,了解垃圾收集行为至关重要,以避免潜在的内存泄...
    编程 发布于2025-05-20
  • 如何有效地选择熊猫数据框中的列?
    如何有效地选择熊猫数据框中的列?
    在处理数据操作任务时,在Pandas DataFrames 中选择列时,选择特定列的必要条件是必要的。在Pandas中,选择列的各种选项。选项1:使用列名 如果已知列索引,请使用ILOC函数选择它们。请注意,python索引基于零。 df1 = df.iloc [:,0:2]#使用索引0和1 c...
    编程 发布于2025-05-20
  • 如何在php中使用卷发发送原始帖子请求?
    如何在php中使用卷发发送原始帖子请求?
    如何使用php 创建请求来发送原始帖子请求,开始使用curl_init()开始初始化curl session。然后,配置以下选项: curlopt_url:请求 [要发送的原始数据指定内容类型,为原始的帖子请求指定身体的内容类型很重要。在这种情况下,它是文本/平原。要执行此操作,请使用包含以下标头...
    编程 发布于2025-05-20
  • 为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    The Mystery of "Broken" Two-Phase Template Instantiation in Microsoft Visual C Problem Statement:Users commonly express concerns that Micro...
    编程 发布于2025-05-20
  • 可以在纯CS中将多个粘性元素彼此堆叠在一起吗?
    可以在纯CS中将多个粘性元素彼此堆叠在一起吗?
    [2这里: https://webthemez.com/demo/sticky-multi-header-scroll/index.html &lt;/main&gt; &lt;section&gt; { display:grid; grid-template-...
    编程 发布于2025-05-20
  • 为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
    为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
    查询模式实现缺失:解决“无法找到”错误在Silverlight应用程序中,尝试使用LINQ建立LINQ连接以错误而实现的数据库”,无法找到查询模式的实现。”当省略LINQ名称空间或查询类型缺少IEnumerable 实现时,通常会发生此错误。 解决问题来验证该类型的质量是至关重要的。在此特定实例中...
    编程 发布于2025-05-20

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3