I want to run an external script (demo_print.py) and print the output in real-time in a text widget.
I got error:
What's my mistake and how to reach my goal ? You can suggest more simple solution if you have.
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/bin/python3/3.7.4/lib/python3.7/threading.py", line 926, in _bootstrap_inner
self.run()
File "/usr/bin/python3/3.7.4/lib/python3.7/threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "example_gui.py", line 37, in test
textbox.insert(tk.END, msg "\n")
File "example_gui.py", line 20, in write
self.widget.insert('end', textbox)
File "/usr/bin/python3/3.7.4/lib/python3.7/tkinter/__init__.py", line 3272, in insert
self.tk.call((self._w, 'insert', index, chars) args)
_tkinter.TclError: out of stack space (infinite loop?)
I want to run an external script (demo_print.py) and print the output in real-time in a text widget.
example_gui.py
import tkinter as tk
import subprocess
import threading
import sys
from functools import partial
# ### classes ####
class Redirect:
def __init__(self, widget, autoscroll=True):
self.widget = widget
self.autoscroll = autoscroll
def write(self, textbox):
self.widget.insert('end', textbox)
if self.autoscroll:
self.widget.see('end') # autoscroll
def flush(self):
pass
def run(textbox=None):
threading.Thread(target=test, args=[textbox]).start()
def test(textbox=None):
p = subprocess.Popen("demo_print.py", 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:
textbox.insert(tk.END, msg "\n")
if __name__ == "__main__":
fenster = tk.Tk()
fenster.title("My Program")
textbox = tk.Text(fenster)
textbox.grid()
scrollbar = tk.Scrollbar(fenster, orient=tk.VERTICAL)
scrollbar.grid()
textbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=textbox.yview)
start_button = tk.Button(fenster, text="Start", command=partial(run, textbox))
start_button.grid()
old_stdout = sys.stdout
sys.stdout = Redirect(textbox)
fenster.mainloop()
sys.stdout = old_stdout
demo_print.py
import time
for i in range(10):
print(f"print {i}")
time.sleep(1)
CodePudding user response:
Okay so first of all make sure demo_print.py is in the same space as your main.py not in a folder or anything then you can just do this:
from demo_print import *
print(whatever u named your output variable in demo_print)
From the looks of it you know how to do the rest.
CodePudding user response:
Since you execute "demo_print.py"
directly, so it must be executable and has correct shebang (for example, #!/usr/bin/python -u
) in the file.
However, I would suggest to execute the file using Python executable instead. Also the Redirect
class is not necessary for your case.
Below is the modified code:
import tkinter as tk
import subprocess
import threading
import sys
from functools import partial
def run(textbox=None):
threading.Thread(target=test, args=[textbox]).start()
def test(textbox=None):
# using the Python executable to run demo_print.py
p = subprocess.Popen([sys.executable, "-u", "demo_print.py"], 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:
textbox.insert(tk.END, msg "\n")
textbox.see(tk.END)
if __name__ == "__main__":
fenster = tk.Tk()
fenster.title("My Program")
textbox = tk.Text(fenster)
textbox.grid(row=0, column=0)
scrollbar = tk.Scrollbar(fenster, orient=tk.VERTICAL)
scrollbar.grid(row=0, column=1, sticky="ns")
textbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=textbox.yview)
start_button = tk.Button(fenster, text="Start", command=partial(run, textbox))
start_button.grid()
fenster.mainloop()