Python tkinter поддерживает графический интерфейс и чтение из канала

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

import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename
import subprocess
import threading
import time
from queue import *

class Application:
    
    # load main window of application
    def __init__(self, master):
        self.current_script_file_name = None
        self.q = Queue()

        self.window = master
        self.window.title("TK")
        self.window.rowconfigure(1, minsize = 600, weight = 2)
        self.window.columnconfigure(1, minsize = 600, weight= 2)

        # create frame for buttons and create buttons
        self.frame_buttons = tk.Frame(master = self.window, relief = tk.RAISED, bd = 2)
        self.button_save = tk.Button(master = self.frame_buttons, text = "Save", command = self.save_script_to_file)
        self.button_run = tk.Button(master = self.frame_buttons, text = "Run", command = self.run_script_from_file)

        # create frame for tk.Text editor and output
        self.frame_text = tk.Frame(master = self.window, relief = tk.RAISED, bd = 2)
        self.text_editor = tk.Text(self.frame_text)
        self.text_output = tk.Text(self.frame_text, background = "Black", foreground = "White")

        #adjust buttons
        self.button_save.grid(row = 0, column = 0, sticky = "ew", padx = 5, pady = 5)
        self.button_run.grid(row = 1, column = 0, sticky = "ew", padx = 5)
        self.frame_buttons.grid(row = 0, column = 0, sticky = "ns")

        #adjust text editor and text output
        self.text_editor.grid(row = 0, column = 0, sticky = "ew", padx = 10, pady = 10)
        self.text_output.grid(row = 1, column = 0, sticky = "ew", padx = 5, pady = 5)
        self.frame_text.grid(row = 0, column = 1, sticky = "ns")

        self.text_output.insert(tk.END, 'Script Result:\n')
        
    def run(self):
        self.window.mainloop()

    def save_script_to_file(self):
        file_path = asksaveasfilename(
            filetypes=[("Python Scripts", "*.py"), ("Kotlin Scripts", "*.kts*")]
        )
        if not file_path:
            return
        with open(file_path, "w") as output_file:
            text = self.text_editor.get(1.0, tk.END)
            output_file.write("#!/usr/bin/env python3\n")
            output_file.write(text)
            
        self.window.title(f"Text Editor Application - {file_path}")


    def run_script_from_file(self):
        # start thread so main window not going to freeze
        threading.Thread(target=self.run_script).start()
        self.update()



    def run_script(self):
        sub_proc = subprocess.Popen(['python','script.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

        threading.Thread(target=self.pipe_reader, args=[sub_proc.stdout]).start()
        threading.Thread(target=self.pipe_reader, args=[sub_proc.stderr]).start()

    def update(self):
        while not self.q.empty():
            for source, line in iter(self.q.get, None):
                self.text_output.insert(tk.END,line)
        self.window.after(1000,self.update)


    def pipe_reader(self, pipe):
        try:
            with pipe:
                for line in iter(pipe.readline, b''):
                    self.q.put((pipe, line))
        finally:
            self.q.put(None)

if __name__ == '__main__':
    root = tk.Tk()
    app = Application(root)
    app.run()

run_script_from_file — это команда кнопки. И self.q - это очередь, принадлежащая к тому же классу этих функций.

Почему мое поле text_output tk.Text не обновляется как можно скорее

Скрипт.py:

#!/usr/bin/env python3

import time

counter = 1
while counter < 6:
    print ("The current counter value: %d" % counter)
    counter = counter + 1
    time.sleep(1)


print(asd)

Обновлено: я поместил весь свой код Обновлено: добавлен script.py

Этого кода недостаточно, чтобы дать определенный ответ, но я предполагаю, что это потому, что вы пропустили flush=True из другого скрипта, что приводит к буферизации вывода. Почему вы делаете это через подпроцесс, а не просто import script?

Novel 18.12.2020 03:06

Это почти весь код, который я написал на самом деле. Я как бы пытаюсь реализовать что-то вроде CMake. В графическом интерфейсе он создает подпроцесс, запускает скрипт и показывает вывод в другом поле.

berkeyvx 18.12.2020 03:13

Вам нужно показать минимальный воспроизводимый пример, прежде чем мы сможем оказать конкретную помощь. Он должен включать в себя другую программу, если это тоже то, что вы написали. Я не понимаю ваш комментарий cmake. Если вы пытаетесь запустить другую программу на Python параллельно, вам не нужен подпроцесс, вы можете просто импортировать другой файл и запустить импорт в другом потоке или процессе или как часть основного цикла tkinter (в зависимости от того, что он делает).

Novel 18.12.2020 03:20

Этот пример с CMake был плохим. Я хочу запустить скрипт с другого языка и одновременно видеть вывод во время выполнения скрипта.

berkeyvx 18.12.2020 03:26
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
4
214
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Хорошо, так что это на самом деле не так уж и далеко. Одна общая ошибка: из-за буферизации вам нужно запускать python в режиме -u. Единственная другая вещь - это постоянно запускать цикл обновления tkinter. Попробуй это:

import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename
import subprocess
import threading
import time
from queue import *

class Application:

    # load main window of application
    def __init__(self, master):
        self.current_script_file_name = None
        self.q = Queue()

        self.window = master
        self.window.title("TK")
        self.window.rowconfigure(1, minsize = 600, weight = 2)
        self.window.columnconfigure(1, minsize = 600, weight= 2)

        # create frame for buttons and create buttons
        self.frame_buttons = tk.Frame(master = self.window, relief = tk.RAISED, bd = 2)
        self.button_save = tk.Button(master = self.frame_buttons, text = "Save", command = self.save_script_to_file)
        self.button_run = tk.Button(master = self.frame_buttons, text = "Run", command = self.run_script_from_file)

        # create frame for tk.Text editor and output
        self.frame_text = tk.Frame(master = self.window, relief = tk.RAISED, bd = 2)
        self.text_editor = tk.Text(self.frame_text)
        self.text_output = tk.Text(self.frame_text, background = "Black", foreground = "White")

        #adjust buttons
        self.button_save.grid(row = 0, column = 0, sticky = "ew", padx = 5, pady = 5)
        self.button_run.grid(row = 1, column = 0, sticky = "ew", padx = 5)
        self.frame_buttons.grid(row = 0, column = 0, sticky = "ns")

        #adjust text editor and text output
        self.text_editor.grid(row = 0, column = 0, sticky = "ew", padx = 10, pady = 10)
        self.text_output.grid(row = 1, column = 0, sticky = "ew", padx = 5, pady = 5)
        self.frame_text.grid(row = 0, column = 1, sticky = "ns")

        self.text_output.insert(tk.END, 'Script Result:\n')

        self.update()

    def save_script_to_file(self):
        file_path = asksaveasfilename(
            filetypes=[("Python Scripts", "*.py"), ("Kotlin Scripts", "*.kts*")]
        )
        if not file_path:
            return
        with open(file_path, "w") as output_file:
            text = self.text_editor.get(1.0, tk.END)
            output_file.write("#!/usr/bin/env python3\n")
            output_file.write(text)

        self.window.title(f"Text Editor Application - {file_path}")

    def run_script_from_file(self):
        sub_proc = subprocess.Popen(['python3', '-u','script.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        threading.Thread(target=self.pipe_reader, args=[sub_proc.stdout]).start()
        threading.Thread(target=self.pipe_reader, args=[sub_proc.stderr]).start()

    def update(self):
        while not self.q.empty():
            source, line = self.q.get()
            if line is None:
                line = "DONE"
            self.text_output.insert(tk.END,line)
        self.window.after(100, self.update)

    def pipe_reader(self, pipe):
        for line in iter(pipe.readline, b''):
            self.q.put((pipe, line))
        self.q.put((pipe, None))

if __name__ == '__main__':
    root = tk.Tk()
    app = Application(root)
    root.mainloop()

Отлично спасибо. Можете ли вы объяснить, почему вы удалили app.run и изменили функцию обновления?

berkeyvx 18.12.2020 04:38

Просто потому, что они не нужны.

Novel 18.12.2020 04:48

Другие вопросы по теме