简体   繁体   中英

Redirect terminal output to tkinter

I'm trying to make a very simple GUI for my program(here i have replaced it with the test function). I'm able to run my code via the gui, but the output for the program is being displayed in the terminal. I want the output to be visible in my original window instead.

Here is my code:

import tkinter as tk
from test import *
from tkinter import *
import os

root = tk.Tk()

canvas = tk.Canvas(root, height=400, width =550, bg ="#202020") 
canvas.pack()

#frame = tk.Frame(root, bg="white");
#frame.place(relwidth=0.8, relheight = 0.8, relx=0.1, rely=0.1)

topFrame = tk.Frame(root, bg="#202020")
topFrame.place(relwidth=1, relheight = 0.75)

bottomFrame = tk.Frame(root, bg="#202020")
bottomFrame.place(rely=0.75, relwidth=1, relheight = 0.25)


launch = tk.Button(bottomFrame, text="Launch", bg="white", fg="#202020", font="noah 10 bold", padx=20, command=test)
launch.place(in_=bottomFrame, rely=0.5, relx=0.5, anchor=CENTER)

root.mainloop()

and here is the other file I'm calling the function from:

import os
def test():
    print("Hello World")
    os.system("ping 192.168.0.1 -c 4")

Is there any way to capture the output of this function and display it in the topframe of my gui in realtime?

You can create class with function write() which insert text to tkinter.Text and assign its instance to sys.stdout - and then print() will send to tkinter.Text

class Redirect():

    def __init__(self, widget):
        self.widget = widget

    def write(self, text):
        self.widget.insert('end', text)

    # some widget may need it
    #def flush(self):
    #    pass

and

text = tk.Text(root)
text.pack()

# keep original stdout
old_stdout = sys.stdout    

# assing Redirect with widget Text 
sys.stdout = Redirect(text)

root.mainloop()

# assign back original stdout (if you need it)
sys.stdout = old_stdout

But os.system you will have to replace with ie. subprocess.run() to catch output and print() it.

def test():
    print("Hello World")
    p = subprocess.run("ping -c 4 stackoverflow.com", shell=True, stdout=subprocess.PIPE)
    print(p.stdout.decode())

Minimal working code

import tkinter as tk
import os
import sys
import subprocess

# --- functions ---

def test():
    print("Hello World")
    p = subprocess.run("ping -c 4 stackoverflow.com", shell=True, stdout=subprocess.PIPE)
    print(p.stdout.decode())

# --- classes ---

class Redirect():

    def __init__(self, widget):
        self.widget = widget

    def write(self, text):
        self.widget.insert('end', text)

    #def flush(self):
    #    pass

# --- main ---    

root = tk.Tk()

text = tk.Text(root)
text.pack()

button = tk.Button(root, text='TEST', command=test)
button.pack()

old_stdout = sys.stdout    
sys.stdout = Redirect(text)

root.mainloop()

sys.stdout = old_stdout

Two problems:

It sends text from subprocess to widget Text after getting all text from ping . It may need more work to display line by line.

test() needs some time to finish work and it blocks tkinter so it can't update widgets and it freezes. It may need to run test() in separated thread but I don't know if this will not gives other problems because in many GUI frameworks you can't use widgets in separated threat.


EDIT: Version with threading . It resolvs previous problems.

But it may have new problems:)

  • you can click button two times and it will run two pings at the same time and mix strings from both pings

  • code doesn't have method to stop thread (ie. when you run ping without -c 4 )

Code:

import tkinter as tk
import sys
import subprocess
import threading 

# --- functions ---

def run():
    threading.Thread(target=test).start()

def test():
    print("Hello World")

    p = subprocess.Popen("ping -c 4 stackoverflow.com".split(), stdout=subprocess.PIPE, bufsize=1, text=True)
    while p.poll() is None:
        msg = p.stdout.readline().strip() # read a line from the process output
        if msg:
            print(msg)

    print("Finished")

# --- classes ---

class Redirect():

    def __init__(self, widget):
        self.widget = widget

    def write(self, text):
        self.widget.insert('end', text)

    #def flush(self):
    #    pass

# --- main ---    

root = tk.Tk()

text = tk.Text(root)
text.pack()

button = tk.Button(root, text='TEST', command=run)
button.pack()

old_stdout = sys.stdout    
sys.stdout = Redirect(text)

root.mainloop()

sys.stdout = old_stdout

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM