Python TKinter GUI冻结,直到子流程结束并实时输出到文本小部件

Python TKinter GUI冻结,直到子流程结束并实时输出到文本小部件,python,user-interface,tkinter,subprocess,python-multiprocessing,Python,User Interface,Tkinter,Subprocess,Python Multiprocessing,我正在尝试向我不久前创建的GUI添加一些功能,特别是我需要的功能是一个文本小部件,我发送的终端命令显示它们的输出。 重定向器类目前看起来如下所示: class StdRed(object): def __init__(self, textwid): self.text_space = textwid def write(self, text): self.text_space.config(state=NORMAL) self.t

我正在尝试向我不久前创建的GUI添加一些功能,特别是我需要的功能是一个文本小部件,我发送的终端命令显示它们的输出。 重定向器类目前看起来如下所示:

class StdRed(object):
    def __init__(self, textwid):
        self.text_space = textwid

    def write(self, text):
        self.text_space.config(state=NORMAL)
        self.text_space.insert(END,text)
        self.text_space.see(END)
        self.text_space.update_idletasks()
        self.text_space.config(state=DISABLED)

    def flush(self):
        pass
事实上,它是有效的。我将os.system(…)命令替换为打开终端命令

a=subprocess.Popen(命令,stdout=PIPE,stderr=stdout,shell=True)

我通读stdout:
b=a.stdout.read()
,没有任何问题(不幸的是,我需要shell=True,否则我需要调用的一些程序会失败得很惨)。 之后,我尝试在tkinter文本小部件上获得实时输出,因此我更改了b-->

但是,似乎只有在被调用的进程结束时才显示输出,例如,一个简单的C软件


对于(int i=0;i这里的解决方案是使用线程,否则脚本将等待作业完成,以使GUI再次响应。使用线程,您的程序将同时运行作业和GUI,代码示例:

import threading

def function():
    pass

t = threading.Thread(target=function)
t.daemon = True # close pipe if GUI process exits
t.start()
我使用了这个std重定向器:

class StdRedirector():
    """Class that redirects the stdout and stderr to the GUI console"""
    def __init__(self, text_widget):
        self.text_space = text_widget

    def write(self, string):
        """Updates the console widget with the stdout and stderr output"""
        self.text_space.config(state=NORMAL)
        self.text_space.insert("end", string)
        self.text_space.see("end")
        self.text_space.config(state=DISABLED)

我尝试使用@Pau B建议的线程(最终切换到多处理),我确实解决了GUI卡住的问题


为了(int i=0;i也许这可以帮助其他人,我解决了用endl替换'\n'的问题。while循环中的cout似乎是缓冲的,stdout flush只在一段时间后调用,而endl函数在每个周期后调用。

我很久以前在python中搜索了如何使用线程,但是如果我没记错,您无法确定一个线程,不是吗?出于这个原因,我在GUI中使用了多处理(我也尝试在进程中打开一个子进程,但结果很差)线程在函数完成时终止。上周我不得不在Tkinter GUI中实现这样一个控制台,线程工作得很好!我会试试看,你有没有关于实时问题的线索?实时问题是因为我告诉过你,python将忙于处理进程,只有当它完成后才会继续使用tkinter主循环。对于std重定向,我使用了这个类(编辑了我的答案)。不幸的是,即使这样,程序也不能正常工作。如果你想检查,我用一个示例程序编辑了我的问题。你介意发布完整的答案吗?我不知道endl是什么
#include <iostream>
using namespace std;

int main()
{
    int i = 0;
    while(1)
    {
     cout << i << '\n';
     i++;
     int a = 0;
     while(a < 10E6)
     {
        a++;
     }
    }    
}
import threading

def function():
    pass

t = threading.Thread(target=function)
t.daemon = True # close pipe if GUI process exits
t.start()
class StdRedirector():
    """Class that redirects the stdout and stderr to the GUI console"""
    def __init__(self, text_widget):
        self.text_space = text_widget

    def write(self, string):
        """Updates the console widget with the stdout and stderr output"""
        self.text_space.config(state=NORMAL)
        self.text_space.insert("end", string)
        self.text_space.see("end")
        self.text_space.config(state=DISABLED)
class StdRed(object):
    def __init__(self, textwid):
        self.text_space = textwid

    def write(self, text):
        self.text_space.config(state=NORMAL)
        self.text_space.insert(END,text)
        self.text_space.see(END)
        self.text_space.update_idletasks()
        self.text_space.config(state=DISABLED)

def termexe(execute):
        a = Popen(execute, shell=True, stdout=PIPE, stderr=STDOUT) 
        while True:
            line = a.stdout.readline().rstrip()
            if not line:
                break
            else:
                queue.put(line)    
        queue.put('') 

def labterm():
    global th
    if queue.empty():
        if th != None:
            if th.is_alive():
                root.after(0,labterm)
            else:
                pass    
        else:
            pass                    
    else:
        q = queue.get()        
        print q
        root.after(1,labterm)
def comter(event=None):
    global enter   
    global th
    if th != None:
        if not th.is_alive():
            th = Process(target=termexe, args=(enter.get(),))
            th.start()
        else:
            pass 
    else:    
        th = Process(target=termexe, args=(enter.get(),))
        th.start()      
    enter.set('')
    labterm()