Python cmd模块-异步事件后恢复提示
我正在维护一个基于cmd的操作员终端。客户要求进行提醒行为。e、 g.发生异步事件时屏幕上显示的消息。我创建了一个线程,定期检查警报,当它发现一些警报时,它只会将它们打印到标准输出 这似乎工作正常,但它似乎不是很优雅,它有一个问题: 由于cmd不知道发生了警报,因此该消息在屏幕上显示为空白。不会重新打印命令提示,任何用户输入都将挂起 有没有更好的方法在Python cmd期间执行异步警报?使用现有的方法,我可以中断cmd并让它重新绘制其提示符吗 我试图从我的线程中使用StringIO在stdin中插入一条新行,但这并不理想,而且我还没有让它正常工作 示例代码:Python cmd模块-异步事件后恢复提示,python,asynchronous,cmd,alert,Python,Asynchronous,Cmd,Alert,我正在维护一个基于cmd的操作员终端。客户要求进行提醒行为。e、 g.发生异步事件时屏幕上显示的消息。我创建了一个线程,定期检查警报,当它发现一些警报时,它只会将它们打印到标准输出 这似乎工作正常,但它似乎不是很优雅,它有一个问题: 由于cmd不知道发生了警报,因此该消息在屏幕上显示为空白。不会重新打印命令提示,任何用户输入都将挂起 有没有更好的方法在Python cmd期间执行异步警报?使用现有的方法,我可以中断cmd并让它重新绘制其提示符吗 我试图从我的线程中使用StringIO在stdin
import cmd, sys
import threading, time
import io
import sys
class MyShell(cmd.Cmd):
intro = '*** Terminal ***\nType help or ? to list commands.\n'
prompt = '> '
file = None
def alert(self):
time.sleep(5)
print ('\n\n*** ALERT!\n')
sys.stdin = io.StringIO("\n")
def do_bye(self, arg):
'Stop recording, close the terminal, and exit: BYE'
print('Exiting.')
sys.exit(0)
return True
def do_async(self, arg):
'Set a five second timer to pop an alert.'
threading.Thread(target=self.alert).start()
def emptyline(self):
pass
def parse(arg):
'Convert a series of zero or more numbers to an argument tuple'
return tuple(map(int, arg.split()))
if __name__ == '__main__':
MyShell().cmdloop()
最后,我用自己的版本覆盖了Cmd.cmdloop,用自己使用非阻塞终端IO的读线替换了readlines() 此处为非阻塞终端IO信息: 不幸的是,这会带来另一个麻烦,因为它很混乱,并且会破坏自动完成和命令历史记录。幸运的是,客户可以按Enter键重新执行提示,所以我不必再担心了 显示非阻塞终端输入方法的不完整示例代码:
import cmd, sys
import threading, time
import io
import os
if os.name=='nt':
import msvcrt
def getAnyKey():
if msvcrt.kbhit():
return msvcrt.getch()
return None
else:
import sys
import select
import tty
import termios
import atexit
def isData():
return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])
old_settings = termios.tcgetattr(sys.stdin)
def restoreSettings():
global old_settings
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
atexit.register(restoreSettings)
def getAnyKey():
try:
if isData():
return sys.stdin.read(1)
return None
except:
pass
return None
class MyShell(cmd.Cmd):
prompt = '> '
file = None
realstdin = sys.stdin
mocking=False
breakReadLine=False
def alert(self):
time.sleep(5)
print ('\n\n*** ALERT!\n')
self.breakReadLine=True
# ----- basic commands -----
def do_bye(self, arg):
'Stop recording, close the terminal, and exit: BYE'
print('Exiting.')
sys.exit(0)
return True
def do_async(self, arg):
'Set a five second timer to pop an alert.'
threading.Thread(target=self.alert).start()
def emptyline(self):
pass
def myReadLine(self):
sys.stdout.flush()
self.breakReadLine=False
line=''
while not self.breakReadLine:
c=getAnyKey()
if not c is None:
c=c.decode("utf-8")
if c=='\x08' and len(line):
line=line[0:-1]
elif c in ['\r','\n']:
print('\n')
return line
else:
line+=c
print(c,end='')
sys.stdout.flush()
def mycmdloop(self, intro=None):
"""Repeatedly issue a prompt, accept input, parse an initial prefix
off the received input, and dispatch to action methods, passing them
the remainder of the line as argument.
"""
self.preloop()
if self.use_rawinput and self.completekey:
try:
import readline
self.old_completer = readline.get_completer()
readline.set_completer(self.complete)
readline.parse_and_bind(self.completekey+": complete")
except ImportError:
pass
try:
if intro is not None:
self.intro = intro
if self.intro:
self.stdout.write(str(self.intro)+"\n")
stop = None
while not stop:
if self.cmdqueue:
line = self.cmdqueue.pop(0)
else:
if self.use_rawinput:
try:
print(self.prompt,end='')
line = self.myReadLine()#input(self.prompt)
except EOFError:
line = 'EOF'
else:
self.stdout.write(self.prompt)
self.stdout.flush()
line = self.myReadLine()#self.stdin.readline()
if not line is None:
line = line.rstrip('\r\n')
line = self.precmd(line)
stop = self.onecmd(line)
stop = self.postcmd(stop, line)
self.postloop()
finally:
if self.use_rawinput and self.completekey:
try:
import readline
readline.set_completer(self.old_completer)
except ImportError:
pass
def cmdloop_with_keyboard_interrupt(self, intro):
doQuit = False
while doQuit != True:
try:
if intro!='':
cintro=intro
intro=''
self.mycmdloop(cintro)
else:
self.intro=''
self.mycmdloop()
doQuit = True
except KeyboardInterrupt:
sys.stdout.write('\n')
def parse(arg):
'Convert a series of zero or more numbers to an argument tuple'
return tuple(map(int, arg.split()))
if __name__ == '__main__':
#MyShell().cmdloop()
MyShell().cmdloop_with_keyboard_interrupt('*** Terminal ***\nType help or ? to list commands.\n')