有些时候我们想在图形界面中嵌入一个命令行,方便进行交互。但是获取程序的输入和输出是比较困难的,网上也很少有类似的教程。最后我综合了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