Python 利用多线程实时读取subprocess.Popen的程序输出的stdout和stderr,并且向其中实时传入input信息。

有些时候我们想在图形界面中嵌入一个命令行,方便进行交互。但是获取程序的输入和输出是比较困难的,网上也很少有类似的教程。最后我综合了stackoverflow上的一些代码得出了结论。

设想以下的应用场景:想要写一个PythonIDE,这个IDE要带有Shell,也就是一个REPL环境。
因此,实现方案就是用subprocess.Popen创建一个新的Python的命令行解释器,将用户输入的文本发送给这个命令行解释器,然后把这个解释器的输出的文本设法获取到,再从文本显示窗口中显示出来。这样,运行图形界面的解释器只有tkinter这一标准库即可,真正的任务交给Popen创建的新解释器进程,即可避免阻塞。

为了简单起见,这个代码目前只是在终端中输入和输出,目前是python,但是稍作改动即可适用于任何自带REPL的环境,比如julia, bash,cmd,octave等语言。

涉及的点:
python subprocess.Popen实时获取输出;
python subprocess.Popen 实时获取输入信息等。

注意,适用于linux平台,windows平台没有测试,可能无法运行。

import queue
import subprocess
import sys
import threading


def enqueue_stream(stream, queue, type):# 将stderr或者stdout写入到队列q中。
    for line in iter(stream.readline, b''):
        queue.put(str(type) + line.decode('utf-8'))
    stream.close()

q = queue.Queue()


import subprocess
import os
import time
process = subprocess.Popen(['python','-i'],stdin=subprocess.PIPE,
shell=False,stdout=subprocess.PIPE,stderr=subprocess.PIPE)

def consoleLoop():# 封装后的内容。
    idleLoops=0
    while True:
    
    #     print(q.empty())
        if not q.empty():
            line = q.get()
            if line[0] == '2':  # stderr
                sys.stdout.write("\033[0;31m")  # ANSI red color
            sys.stdout.write(line[1:])
            if line[0] == '2':
                sys.stdout.write("\033[0m")  # reset ANSI code
            sys.stdout.flush()
        else:
            time.sleep(0.01)
            if idleLoops>=5:
                idleLoops=0
                inp=input('>>>')+'\n'
                process.stdin.write(inp.encode('utf-8'))#input('>>>').encode('utf-8'))   
                process.stdin.flush() 
                continue
            idleLoops+=1

to = threading.Thread(target=enqueue_stream, args=(process.stdout, q, 1))
te = threading.Thread(target=enqueue_stream, args=(process.stderr, q, 2))
tp = threading.Thread(target=consoleLoop)
to.setDaemon(True)
te.setDaemon(True)
tp.setDaemon(True)
te.start()
to.start()
tp.start()


    
to.join()    
tp.join()
te.join()

while(1):
    time.sleep(2)
    pass
    
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值