Графический интерфейс TKinter зависает до конца подпроцесса и выводит текстовый виджет в реальном времени - PullRequest
0 голосов
/ 03 апреля 2019

Я пытаюсь добавить некоторые функции в графический интерфейс, который я создал некоторое время назад, в частности, мне нужна функция текстового виджета, в которой команды терминала, которые я отправляю, показывают свой вывод.На данный момент класс перенаправителя выглядит следующим образом:

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)

и я читаю стандартный вывод через: b = a.stdout.read() без единой проблемы (к сожалению, мне нужна эта оболочка = True, в противном случае некоторые программы, которые мне нужно вызвать, с треском проваливаются).После этого я попытался получить вывод в реальном времени для текстового виджета tkinter, поэтому я изменил b ->

while True:
    b = a.stdout.readline().rstrip()
    if not b:
        break
    print b 

, но кажется, что вывод появляется только после завершения вызванного процесса, то есть простого программного обеспечения на Cкак

для (int i = 0; i <100000; i ++) {cout << i << '\ n';} </p>

будет печататься очень медленно (Я замечаю медленно, учитывая, что простая команда "ls" будет также очень медленно печатать строку за строкой) все числа в конце цикла for.Кроме того, я заметил, что графический интерфейс пользователя заморожен во время работы программ, вызываемых через подпроцесс.Есть идеи, как решить эти проблемы?

РЕДАКТИРОВАТЬ :

Я создал простой терминал, который запускает команды, используя класс многопроцессорной обработки, и Popen:

from Tkinter import *
from multiprocessing import Process, Pipe, Queue
import sys
from subprocess import PIPE, Popen, STDOUT

root = Tk()
root.title("Test Terminal")
root.resizable(False, False)

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

terminal = Frame(root, bd=2, relief=GROOVE)
terminal.grid(row=0, sticky='NSEW')
TERM = Label(terminal, text='TERMINAL', font='Helvetica 16 bold')
TERM.grid(row=0, pady=10, sticky='NSEW')
termwid = Text(terminal, height=10)
termwid.grid(row=1, sticky='NSEW')   
termwid.configure(state=DISABLED, font="Helvetica 12")   
sys.stdout = StdRed(termwid) 
enter = StringVar()
enter.set("")
termen = Entry(terminal, textvariable=enter)
queue = Queue(maxsize=1)
a = None

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(thi):
    if queue.empty():
        if thi != None:
            if thi.is_alive():
                root.after(0,lambda:labterm(thi))
            else:
                pass    
        else:
            pass                    
    else:
        q = queue.get()       
        print q
        root.after(0,lambda:labterm(thi))     


def comter(event=None, exe=None, seq=None):
    global enter   
    if seq == 1:
        if exe != None:     
            th = Process(target=termexe, args=(exe,))
            th.daemon = True
            th.start()
            labterm(th)
            th.join()
        else:
            pass
    else:            
        if exe != None:     
            th = Process(target=termexe, args=(exe,))
            th.daemon = True
            th.start()
            labterm(th)
        else:
            th = Process(target=termexe, args=(enter.get(),))
            th.daemon = True
            th.start()
            enter.set('')        
            labterm(th)

def resetterm():
    global termwid
    termwid.config(state=NORMAL)
    termwid.delete(1.0, END)
    termwid.config(state=DISABLED)    

termen.bind('<Return>', comter)
resterm = Button(terminal, text="Clear", command=resetterm)
terbut = Button(terminal, text="Command", command=comter)
termen.grid(row=2, sticky='NSEW')
terbut.grid(row=3, sticky='NSEW')
resterm.grid(row=4, sticky='NSEW')        

root.mainloop()

Проблема заключается в том, что приобретениевсе еще не в режиме реального времени.Запустив из записи в программном обеспечении программу:

#include <iostream>
using namespace std;

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

некоторое время ни к чему не приводит внутри текстового виджета и через некоторое время вывод появляется внезапно.Есть идеи как решить эту проблему?

Ответы [ 3 ]

1 голос
/ 03 апреля 2019

Решение здесь состоит в том, чтобы использовать многопоточность, в противном случае скрипт ждет, пока работа не будет выполнена, чтобы графический интерфейс снова стал отзывчивым.При работе с потоками ваша программа будет одновременно выполнять задание и графический интерфейс, пример кода:

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)
0 голосов
/ 12 апреля 2019

Может быть, это может помочь другим, я решил проблему, заменив '\ n' на endl. Кажется, что cout в цикле while буферизован, и stdout flush вызывается только через некоторое время, тогда как с endl функция вызывается после каждого цикла

0 голосов
/ 04 апреля 2019

Я попытался использовать потоки в соответствии с предложением @Pau B (в конце концов переключился на многопроцессорность), и я действительно решил проблему зависшего графического интерфейса.Теперь проблема в том, что программа

для (int i = 0; i <100000; i ++) {cout << i << '\ n';} </p>

не возвращает вывод в реальном времени, но кажется, что он буферизируется, а затем появляется через некоторое время в текстовом виджете.Код, который я использую, выглядит следующим образом:

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()

, где comter () вызывается кнопкой или связывается с «Return» внутри текстовой записи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...