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)
#self.widget.see('end') # autoscroll
# 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)
#self.widget.see('end') # autoscroll
#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 :)
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)
#self.widget.see('end') # autoscroll
#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
EDIT: 2021.08.19
Version with Scrollbar and autoscroll
import tkinter as tk
import sys
import subprocess
import threading
# --- classes ---
class Redirect():
def __init__(self, widget, autoscroll=True):
self.widget = widget
self.autoscroll = autoscroll
def write(self, text):
self.widget.insert('end', text)
if self.autoscroll:
self.widget.see("end") # autoscroll
#def flush(self):
# pass
# --- functions ---
def run():
threading.Thread(target=test).start()
def test():
print("Thread: start")
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("Thread: end")
# --- main ---
root = tk.Tk()
# - Frame with Text and Scrollbar -
frame = tk.Frame(root)
frame.pack(expand=True, fill='both')
text = tk.Text(frame)
text.pack(side='left', fill='both', expand=True)
scrollbar = tk.Scrollbar(frame)
scrollbar.pack(side='right', fill='y')
text['yscrollcommand'] = scrollbar.set
scrollbar['command'] = text.yview
old_stdout = sys.stdout
sys.stdout = Redirect(text)
# - rest -
button = tk.Button(root, text='TEST', command=run)
button.pack()
root.mainloop()
# - after close window -
sys.stdout = old_stdout
tkinterwidgets likeTextorLabelto display it - and thenlabel["text"] = "Hello World". Eventually you can assign own class tosys.stdoutand it can redirectprint()toLabelorText.pingthen you have to usesubprocess(ie,subprocess.run()) instead ofos.system()to catch displayed data and next you can put it inLabelorText