所以,我有一个我正在研究的小型控制台应用程序,它运行一个Web抓取过程,我希望能够在执行期间给它控制台命令来控制它。要做到这一点,我需要某种形式的非阻塞键盘输入,因为程序可能由于意外错误而自行终止,并且我不希望某些线程挂起并在发生终止时等待输入。
我已将以下内容删除:
import threading
import time
import queue
input_queue = queue.Queue()
command_input_event = threading.Event()
def kbdListener():
global input_queue, command_input_event
kbdInput = ''
while kbdInput.lower() not in ['quit', 'exit', 'stop']:
kbdInput = input("> ")
input_queue.put(kbdInput)
command_input_event.set()
input_queue.join()
listener = threading.Thread(target=kbdListener)
listener.start()
stop = False
while not stop:
if command_input_event.is_set():
while not input_queue.empty():
command = input_queue.get()
if command.lower() in ['quit', 'exit', 'stop']:
print('Stopping')
while not input_queue.empty():
input_queue.get()
input_queue.task_done()
input_queue.task_done()
stop = True
break
else:
print('Command "{}" received and processed'.format(command))
input_queue.task_done()
我的问题是,在while not stop:
行上,我的程序中会检查另一个条件,它确定主循环是否已经终止。如果发生这种可能性,那么主线程将停止,但后台listener
线程仍将等待输入;我想避免的情况。
我没有参与这种方法,所以如果有一些替代方法可以获得非阻塞输入,那么我也会接受这个建议。
答案 0 :(得分:0)
因此,经过一些工作后,我想出了以下内容,它允许后台线程输入,然后由前台线程处理,但完全无阻塞,并在用户实时输入时进行更新。
import threading
import queue
import sys
from msvcrt import getch, kbhit
"""
Optional extra that I found necessary to get ANSI commands working on windows:
import colorama
colorama.init()
"""
class ClassWithNonBlockingInput:
def __init__(self):
self.command_queue = queue.Queue()
self.command_available_event = threading.Event()
self.stop_event = threading.Event()
def run(self):
main_loop = threading.Thread(target=self.main_loop)
main_loop.start()
listener = threading.Thread(target=self.keyboard_listener)
listener.start()
stop = False
while main_loop.is_alive() and not stop:
if self.command_available_event.is_set():
while not self.command_queue.empty():
command = self.command_queue.get()
#Process command here, long jobs should be on a seperate thread.
self.command_queue.task_done()
self.command_available_event.clear()
def main_loop(self):
#Main processing loop, may set self.stop_event at some point to terminate early.
pass
def keyboard_listener(self):
line = []
line_changed = False
while not self.stop_event.is_set():
while kbhit():
c = getch()
if c == b'\x00' or c == b'\xe0':
#This is a special function key such as F1 or the up arrow.
#There is a second identifier character to clear/identify the key.
id = getch()
#Process the control key by sending commands as necessary.
elif c == b'\x08':
#Backspace character, remove last character.
if len(line) > 0:
line = line[:-1]
line_changed = True
elif c == b'\x7f':
#ctrl-backspace, remove characters until last space.
while len(line) > 0 and line[-1] != b' ':
line = line[:-1]
line_changed = True
elif c == b'\x1b':
#Escacpe key, process as necessary.
pass
elif c == b'\r':
#Enter key, send command to main thread.
print()
command = b''.join(line).decode('utf-8')
self.command_queue.put(command)
self.command_available_event.set()
self.command_queue.join()
line = []
line_changed = True
else:
#Append all other characters to the current line.
#There may be other special keys which need to be considered,
# this is left as an exercise for the reader :P
line.append(c)
line_changed = True
if line_changed:
#Clear the current output line
print('\033[2K', end='\r')
print(b''.join(line).decode('utf-8'), end='')
sys.stdout.flush()
line_changed = False
这应该给将来遇到这个问题的任何人,并且在这个问题上找到一个良好的开端。