tkinter - Exectuting external python file from gui button and redirecting output into gui text widget - Stack Overflow

admin2025-05-02  1

Created a simple GUI, one button, and one text widget. I want an external python file to run by pressing the button and its output to be displayed in the text widget.

The external file, which simply prints words from a list one word to a line until completed, runs properly with no errors and its output goes to the console. When I press the button though I get the console which I don't want and instead of output there is only a blinking cursor.

The console stays up for the approximate length of time to operate completely, there are time delays involved, so I know it is executing the file, but at the same time the console comes up an error appears in the shell.

Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Lou\AppData\Local\Programs\Python\Python313\Lib\tkinter\__init__.py", line 2068, in __call__
return self.func(*args)
           ~~~~~~~~~^^^^^^^
File "C:\Users\Lou\Desktop\Python Development\Python Programs\8902A Op-Ver\Module Tester.py", line 18, in btn_click
line = subprocess.stdout.readline().decode()
           ^^^^^^^^^^^^^^^^^
AttributeError: module 'subprocess' has no attribute 'stdout'

The gui code:

import tkinter as tk
import subprocess
import sys, inspect 
from subprocess import Popen

root = tk.Tk()
i = tk.PhotoImage(width=1, height=1)

# Title and GUI Size
root.title('Module Tester')
root.geometry("500x300")

# Button function
def btn_click():
    btn_click.path = "Test Modules/Test Module 1.py"
    Popen(["python", btn_click.path], stdout = subprocess.PIPE)
    while True:
        line = subprocess.stdout.readline().decode()
        if not line:
            break
        output_box.insert(tk.END, line)
output_box = tk.Text(root, bd = 1, bg = "#3c3d3d", fg = '#7bfce9',
insertbackground='white', font = ("Segoe UI", 11), height = 5, width = 34)
output_box.place(x = 171, y = 167)

# Button                  
btn1_button = tk.Button(root, image=i, compound='c', width=115, height=20, bd = 1, bg = "#232124", fg = "#a571e5", activebackground="#a571e5", activeforeground="#232124", text = "Test Module 1", font = ("Segoe UI", 11), padx=0, pady=0, command=btn_click) 
btn1_button.place(x = 10, y = 20)

root.mainloop()

I understand I have created a function that is calling on my external file and is supposed to redirect the output of my external file and uses a sub process to convert it from streamed output to a python string and I have defined a place for the output to go. I'm not sure what else is happening as I am still fairly new to coding in python and don't have a grasp on all the details yet.

Created a simple GUI, one button, and one text widget. I want an external python file to run by pressing the button and its output to be displayed in the text widget.

The external file, which simply prints words from a list one word to a line until completed, runs properly with no errors and its output goes to the console. When I press the button though I get the console which I don't want and instead of output there is only a blinking cursor.

The console stays up for the approximate length of time to operate completely, there are time delays involved, so I know it is executing the file, but at the same time the console comes up an error appears in the shell.

Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Lou\AppData\Local\Programs\Python\Python313\Lib\tkinter\__init__.py", line 2068, in __call__
return self.func(*args)
           ~~~~~~~~~^^^^^^^
File "C:\Users\Lou\Desktop\Python Development\Python Programs\8902A Op-Ver\Module Tester.py", line 18, in btn_click
line = subprocess.stdout.readline().decode()
           ^^^^^^^^^^^^^^^^^
AttributeError: module 'subprocess' has no attribute 'stdout'

The gui code:

import tkinter as tk
import subprocess
import sys, inspect 
from subprocess import Popen

root = tk.Tk()
i = tk.PhotoImage(width=1, height=1)

# Title and GUI Size
root.title('Module Tester')
root.geometry("500x300")

# Button function
def btn_click():
    btn_click.path = "Test Modules/Test Module 1.py"
    Popen(["python", btn_click.path], stdout = subprocess.PIPE)
    while True:
        line = subprocess.stdout.readline().decode()
        if not line:
            break
        output_box.insert(tk.END, line)
output_box = tk.Text(root, bd = 1, bg = "#3c3d3d", fg = '#7bfce9',
insertbackground='white', font = ("Segoe UI", 11), height = 5, width = 34)
output_box.place(x = 171, y = 167)

# Button                  
btn1_button = tk.Button(root, image=i, compound='c', width=115, height=20, bd = 1, bg = "#232124", fg = "#a571e5", activebackground="#a571e5", activeforeground="#232124", text = "Test Module 1", font = ("Segoe UI", 11), padx=0, pady=0, command=btn_click) 
btn1_button.place(x = 10, y = 20)

root.mainloop()

I understand I have created a function that is calling on my external file and is supposed to redirect the output of my external file and uses a sub process to convert it from streamed output to a python string and I have defined a place for the output to go. I'm not sure what else is happening as I am still fairly new to coding in python and don't have a grasp on all the details yet.

Share Improve this question edited Jan 2 at 9:18 user4136999 asked Jan 2 at 2:01 LouLou 211 silver badge1 bronze badge
Add a comment  | 

2 Answers 2

Reset to default 1

You are trying to access the stdout attribute of the subprocess module itself, which doesn't exist. Here is what you should do to capture the output.

btn_click.path = "Test Modules/Test Module 1.py"
process = Popen(["python", btn_click.path], stdout = subprocess.PIPE)
output, error = process.communicate()   # reads the output from the pipe
output = output.decode('utf-8')   # output is a string
    

The subprocess.PIPE is used to redirect the subprocess output to a pipe and them the process.communicate() reads the output from the pipe.

That said I think you should use the subprocess.run to capture the output.

btn_click.path = "Test Modules/Test Module 1.py"
result = subprocess.run(["python", btn_click.path], capture_output=True) 
output = result.stdout.decode('utf-8')   # output is a string
    

You need to call .stdout.readline().decode() on the object returned by subprocess.Popen().

Also it is better to:

  • add command line argument "-u" when executing python to use unbuffered stdout and stderr streams
  • add stderr=subprocess.STDOUT to redirect stderr to stdout in order to capture any error messages as well

Below is the updated btn_click():

from subprocess import Popen, PIPE, STDOUT
...

def btn_click():
    btn_click.path = "Test Modules/Test Module 1.py"
    # added "-u" when executing python
    # added stderr=STDOUT to redirect stderr to stdout
    # store the return object of Popen()
    proc = Popen(["python", "-u", btn_click.path], stdout=PIPE, stderr=STDOUT)
    line = proc.stdout.readline().decode()
    while line:
        output_box.insert(tk.END, line)
        output_box.see(tk.END)
        output_box.update()
        line = proc.stdout.readline().decode()
    # show completion
    output_box.insert(tk.END, "----- end -----")
    output_box.see(tk.END)
转载请注明原文地址:http://www.anycun.com/QandA/1746136899a92083.html