Python cmd模块-异步事件后恢复提示

Python cmd模块-异步事件后恢复提示,python,asynchronous,cmd,alert,Python,Asynchronous,Cmd,Alert,我正在维护一个基于cmd的操作员终端。客户要求进行提醒行为。e、 g.发生异步事件时屏幕上显示的消息。我创建了一个线程,定期检查警报,当它发现一些警报时,它只会将它们打印到标准输出 这似乎工作正常,但它似乎不是很优雅,它有一个问题: 由于cmd不知道发生了警报,因此该消息在屏幕上显示为空白。不会重新打印命令提示,任何用户输入都将挂起 有没有更好的方法在Python cmd期间执行异步警报?使用现有的方法,我可以中断cmd并让它重新绘制其提示符吗 我试图从我的线程中使用StringIO在stdin

我正在维护一个基于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')