[Tkinter] Defining a self made module's function to interact with the main .py variables? - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: GUI (https://python-forum.io/forum-10.html) +--- Thread: [Tkinter] Defining a self made module's function to interact with the main .py variables? (/thread-27434.html) |
Defining a self made module's function to interact with the main .py variables? - Gilush - Jun-07-2020 I'm using Tkinter. (self learner) My goal is to display the function's output in the main .py text widget. How can I make the function send the output to the text box in the main .py file? ** UPDATE 2 ** ** UPDATE ** i've updated the code and now I get that: Main .py: import tkinter as tk import multiprocessing as mp import os import testping import sys class GUI(tk.Tk): def __init__(self, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) self.geometry("860x600") self.title("GUI Project") menu = tk.Frame(self, relief="solid") container = tk.Frame(self, relief="solid") menu.pack(side="left", fill="y") container.pack(side="right", fill="y", expand=True) menu.grid_rowconfigure(0, weight=1) container.grid_rowconfigure(0, weight=1) container.grid_rowconfigure(1, weight=1) container.grid_rowconfigure(2, weight=1) container.grid_rowconfigure(3, weight=1) container.grid_rowconfigure(4, weight=1) container.grid_rowconfigure(5, weight=1) container.grid_rowconfigure(6, weight=1) container.grid_columnconfigure(0, weight=1) container.grid_columnconfigure(1, weight=2) container.grid_columnconfigure(2, weight=2) container.grid_columnconfigure(3, weight=2) container.grid_columnconfigure(4, weight=2) container.grid_columnconfigure(5, weight=2) container.grid_columnconfigure(6, weight=2) self.frames = ["Menu", "TestPing", "FutureFeature", "FutureFeature2", "FutureFeature3"] self.frames[0] = Menu(parent=menu, controller=self) self.frames[1] = TestPing(parent=container, controller=self) self.frames[2] = FutureFeature(parent=container, controller=self) self.frames[3] = FutureFeature2(parent=container, controller=self) self.frames[4] = FutureFeature3(parent=container, controller=self) self.frames[0].grid(row=0, column=0, sticky="nsew") self.frames[1].grid(row=0, column=0, sticky="nsew") self.frames[2].grid(row=0, column=0, sticky="nsew") self.frames[3].grid(row=0, column=0, sticky="nsew") self.frames[4].grid(row=0, column=0, sticky="nsew") self.show_frame(1) def show_frame(self, page_name): frame = self.frames[page_name] print(frame) frame.tkraise() frame.grid(row=0, column=0, sticky="nsew") class Menu(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller ping_test_button = tk.Button(self, text="Ping Test", bg="skyblue1", pady=30, command=lambda: controller.show_frame(1)) future_feature1_button = tk.Button(self, text="FutureFeature", bg="dark violet", pady=30, command=lambda: controller.show_frame(2)) future_feature2_button = tk.Button(self, text="FutureFeature2", bg="pale goldenrod", pady=30, command=lambda: controller.show_frame(3)) future_feature3_button = tk.Button(self, text="FutureFeature3", bg="green", pady=30, command=lambda: controller.show_frame(4)) app_exit = tk.Button(self, text="Quit", bg="gray40", pady=30, command=lambda: self.terminate()) ping_test_button.pack(fill="both", expand=True) future_feature1_button.pack(fill="both", expand=True) future_feature2_button.pack(fill="both", expand=True) future_feature3_button.pack(fill="both", expand=True) app_exit.pack(fill="both", expand=True) def terminate(self): while True: path = fr'c:/users/{os.getlogin()}/desktop/Gui-Skeleton' try: os.rmdir(path) exit() except OSError as err: print(f"Error Deleting tmp folder! {err}") exit() class TestPing(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent, bg="skyblue1") self.controller = controller self.clear_file = tk.BooleanVar() self.clear_file.set(False) self.url_label = tk.Label(self, text="Enter URL : ", padx=7, pady=5, bg="skyblue1") self.url_input_box = tk.Entry(self) self.file_name_label = tk.Label(self, text="Enter Filename: ", bg="skyblue1") self.file_name_input_box = tk.Entry(self) self.clear_file_label = tk.Label(self, text="Clear File?", padx=5, pady=5, bg="skyblue1") self.clear_file_radio_yes = tk.Radiobutton(self, text="yes", value=True, var=self.clear_file, bg="skyblue1", command=lambda: self.callback(self.clear_file.get())) self.clear_file_radio_no = tk.Radiobutton(self, text="no", value=False, var=self.clear_file, bg="skyblue1", command=lambda: self.callback(self.clear_file.get())) self.submit_button = tk.Button(self, text="Submit", width=10, height=1, command=lambda: self.condition(self.url_input_box.get(), self.clear_file.get(), self.file_name_input_box.get())) self.clear_fields_button = tk.Button(self, text="Clear Fields", width=10, command=lambda: self.clear_boxes(self.url_input_box, self.file_name_input_box)) self.text_input = tk.StringVar() self.text_window = tk.Text(self, height=100, width=500, bg="gray") self.clear_window_button = tk.Button(self, text="Clear Window", width=10, command=lambda: self.clean_window()) self.grid_columnconfigure(0, minsize=50, weight=0) self.grid_columnconfigure(1, minsize=20, weight=2) self.grid_columnconfigure(2, minsize=50, weight=3) self.grid_columnconfigure(3, minsize=20, weight=2) self.grid_rowconfigure(0, minsize=50, weight=0) self.grid_rowconfigure(1, minsize=20, weight=0) self.grid_rowconfigure(2, minsize=30, weight=0) self.grid_rowconfigure(3, minsize=10, weight=0) self.grid_rowconfigure(4, minsize=10, weight=0) self.grid_rowconfigure(5, minsize=10, weight=0) self.url_label.grid(row=0, column=0, sticky="w") self.url_input_box.grid(row=0, column=1, sticky="w") self.file_name_label.grid(row=1, column=0, sticky="w") self.file_name_input_box.grid(row=1, column=1, sticky="w") self.clear_file_label.grid(row=0, column=1, sticky="n") self.clear_file_radio_yes.grid(row=1, column=1) self.clear_file_radio_no.grid(row=2, column=1) self.submit_button.grid(row=3, column=1, sticky="se") self.clear_fields_button.grid(row=3, column=1, sticky="sw") self.text_window.grid(row=7, column=1, rowspan=3, sticky="we") self.clear_window_button.grid(row=8, column=0) def clear_boxes(self, url_input, filename): url_input.delete(0, "end") filename.delete(0, "end") def callback(self, clear): print(f'Clear file = {clear}') # Debugging Mode - check Radio box Var. def condition(self, host, clear, filename): print(clear, filename) # Debugging - Input Validation if clear is True and filename == '': self.handler_clr_yes_fn_no(host) elif clear is False and filename == '': self.handler_clr_no_fn_no(host) elif clear is True and filename != '': self.handler_clr_yes_fn_yes(host, filenameInputBox) elif clear is False and filename != '': self.handler_clr_no_fn_yes(host, filenameInputBox) def handler_clr_yes_fn_no(self, host): startprocs = [] lastprocs = [] proc1 = mp.Process(name="Clear + No Filename + WriteFile", target=testping.clr_yes_fn_no_writefile, args=(host,)) proc2 = mp.Process(name="Clear + No Filename + PrintOutput", target=testping.clr_yes_fn_no_print_output, args=(host,)) proc3 = mp.Process(name="Clear + No Filename + Generate PrintOutput to GUI", target=testping.clr_yes_fn_no_print_to_gui, args=(host,)) proc4 = mp.Process(name="Remove first line + Write new default file", target=testping.delete_default_lines) startprocs.append(proc1) startprocs.append(proc2) startprocs.append(proc3) lastprocs.append(proc4) for s in startprocs: s.start() for s2 in startprocs: s2.join() for l in lastprocs: l.start() def handler_clr_no_fn_no(self, host): procs = [] nextprocs = [] proc1 = mp.Process(name="Append to default file", target=testping.clr_no_fn_no_writefile, args=(host,)) proc2 = mp.Process(name="Print Output", target=testping.clr_no_fn_no_printoutput, args=(host,)) procs.append(proc1) procs.append(proc2) for proc in procs: proc.start() for proc in procs: proc.join() for p in nextprocs: p.start() def handler_clr_yes_fn_yes(self, host, filenameInputBox): procs = [] nextprocs = [] proc1 = mp.Process(name="Clear file + userFilename + Write to file", target=testping.clr_yes_fn_yes_writefile, args=(host, filenameInputBox,)) proc2 = mp.Process(name="Clear file + user filename + Print output", target=testping.clr_yes_fn_yes_printoutput, args=(host,)) proc3 = mp.Process(name="Remove Empty Lines from user filename", target=testping.delete_userfile_lines, args=(filenameInputBox,)) procs.append(proc1) procs.append(proc2) nextprocs.append(proc3) for proc in procs: proc.start() for p in procs: p.join() for np in nextprocs: np.start() def handler_clr_no_fn_yes(self, host, filenameInputBox): procs = [] proc1 = mp.Process(name="Keep File + Userfilename + Append to Userfile", target=testping.clr_no_fn_yes_writefile, args=(host, filenameInputBox,)) proc2 = mp.Process(name="Keep File + Userfilename + Print Output", target=testping.clr_no_fn_yes_printoutput, args=(host,)) procs.append(proc1) procs.append(proc2) for p in procs: p.start() for p2 in procs: p2.join() class FutureFeature(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent, bg="mediumpurple1") self.controller = controller # can be used to run self.controller.show_frame[] class FutureFeature2(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent, bg="light goldenrod") self.controller = controller class FutureFeature3(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent, bg="green") self.controller = controller def check_path(): path = fr'c:/users/{os.getlogin()}/desktop/Gui-Skeleton' try: if os.path.isdir(path): pass else: os.mkdir(path) pass except OSError as err: print(f"[!] Operation failed! {err}") if __name__ == "__main__": check_path() app = GUI() app.mainloop()Module testping.py: import subprocess as sub import multiprocessing as mp import time import os import sys import tkinter as tk clear_file = False host = "" filename = "" text_box = tk.StringVar() old_sys = sys.stdout def delete_default_lines(): time.sleep(1.5) print(f'\nCurrent Proccess: {mp.current_process().name} + {mp.current_process().pid}') file = fr'c:/users/{os.getlogin()}/Desktop/Gui-Skeleton/default-tmp.txt' newfile = fr'c:/users/{os.getlogin()}/Desktop/default.txt' with open(file, 'r') as inp, open(newfile, 'w+') as out: for line in inp: if not line.isspace(): out.write(line.lstrip()) out.write('') inp.close() out.close() os.remove(file) def delete_userfile_lines(filename): time.sleep(1.5) print(f'Current Proccess: {mp.current_process().name} + {mp.current_process().pid}') file = fr'c:/users/{os.getlogin()}/Desktop/Gui-Skeleton/{filename}-tmp.txt' newfile = fr'c:/users/{os.getlogin()}/Desktop/{filename}.txt' with open(file, 'r+') as inp, open(newfile, 'w+') as out: for line in inp: if not line.isspace(): out.write(line.lstrip()) out.write('') inp.close() out.close() os.remove(file) def clr_yes_fn_no_print_output(host): print(f'Current Proccess: {mp.current_process().name} + {mp.current_process().pid}\n') with sub.Popen(["ping", "-n", "4", f'{host}'], stdout=sub.PIPE, bufsize=1, universal_newlines=True, stderr=sub.STDOUT) as p: for line in p.stdout: print(line, end=' ') def clr_yes_fn_no_print_to_gui(host): print(f'Current Proccess: {mp.current_process().name} + {mp.current_process().pid}\n') cmd = ["ping", "-n", "4", f'{host}'] proc = sub.Popen(cmd, stdout=sub.PIPE, shell=True, stderr=sub.STDOUT) for stdout_line in proc.stdout: print(stdout_line.decode(), end='') # yield stdout_line proc.stdout.close() # return_code = proc.wait() # if return_code: # raise sub.CalledProcessError(return_code, cmd) def clr_yes_fn_no_writefile(host): print(f'Current Proccess: {mp.current_process().name} + {mp.current_process().pid}\n') file = fr'c:/users/{os.getlogin()}/Desktop/Gui-Skeleton/default-tmp.txt' ping = sub.Popen(["ping", "-n", '4', f'{host}'], stdout=sub.PIPE) with open(file, 'w+') as output: data = output.read() for line in ping.stdout.readlines(): data += str(line.decode()) ping.stdout.close() output.seek(0) output.write(data.lstrip()) def clr_no_fn_no_printoutput(host): print(f'Current Proccess: {mp.current_process().name} + {mp.current_process().pid}\n') with sub.Popen(["ping", "-n", '4', f'{host}'], stdout=sub.PIPE, bufsize=1, universal_newlines=True, stderr=sub.STDOUT) as p: for line in p.stdout: print(line, end=' ') def clr_no_fn_no_writefile(host): print(f'Current Proccess: {mp.current_process().name} + {mp.current_process().pid}\n') with open(fr'c:/users/{os.getlogin()}/Desktop/default.txt', 'a') as output: sub.call(["ping", "-n", '4', f'{host}'], stdout=output) def clr_yes_fn_yes_printoutput(host): with sub.Popen(["ping", "-n", '4', f'{host}'], stdout=sub.PIPE, bufsize=1, universal_newlines=True, stderr=sub.STDOUT) as p: for line in p.stdout: print(line, end=' ') def clr_yes_fn_yes_writefile(host, filename): file = fr'c:/users/{os.getlogin()}/Desktop/Gui-Skeleton/{filename}-tmp.txt' with open(file, 'w') as output: sub.call(["ping", "-n", '4', f'{host}'], stdout=output) def clr_no_fn_yes_printoutput(host): with sub.Popen(["ping", "-n", '4', f'{host}'], stdout=sub.PIPE, bufsize=1, universal_newlines=True, stderr=sub.STDOUT) as p: for line in p.stdout: print(line, end=' ') def clr_no_fn_yes_writefile(host, filename): with open(fr'c:/users/{os.getlogin()}/Desktop/{filename}.txt', 'a') as output: sub.call(["ping", "-n", '4', f'{host}'], stdout=output)GUI: RE: How can I define a selfmade module's functio to interact with the main .py variables? - Gribouillis - Jun-07-2020 You can add a file handle argument to clr_yes_fn_no_print_output() def clr_fn_no_print_output(host, file=sys.stdout): ... for line in p.stdout: print(line, end=' ', file=file)Then in the code that calls this function you give a StringIO instance as file argument import io ofh = io.StringIO() clr_fn_no_print_output(host, file=ofh) self.text_window.insert(tk.END, ofh.getvalue())It's an example of dependency injection. As far as I know, functions that do input output operations are good candidates for dependency injection. The drawback of this code is that the GUI will freeze during the call to clr_fn_no_print_outut(). To avoid this, you can use the technique with the after() method as I said in your other thread about this subject. RE: How can I define a selfmade module's functio to interact with the main .py variables? - Gilush - Jun-07-2020 Thanks for your time! before I make changes and flow with them and finally nothing will work (i mean nothing!), :) can u please elaborate on the file=file definition? i'll google the rest :) RE: How can I define a selfmade module's functio to interact with the main .py variables? - Gribouillis - Jun-07-2020 Gilush Wrote:before I make changes and flow with them and finally nothing will workThat's a reason why version control systems exist. I hope you're already using one such as git, to keep tracks of previous versions of your code and potentially restore them. Gilush Wrote:can u please elaborate on the file=file definition?It is only the default argument syntax in functions signatures in python >>> spam = "Hello" >>> >>> def func(x=spam): ... print(x) ... >>> func(x='eggs') eggs >>> func() Hello RE: How can I define a selfmade module's functio to interact with the main .py variables? - Gilush - Jun-07-2020 Thanks for replying :) Since my last reply i've added multiprocessing and threads so now i'm having some difficulties with your answer because I have a thread running a process handler that runs the function. main.py: Text widget: def text_widget(self): self.text_window = tk.Text(self, height=100, width=500, bg="gray", wrap=tk.WORD) self.text_window.update() self.text_window.grid(row=7, column=1, rowspan=3, sticky="we") self.text_window.insert(tk.END, ">>>") self.thread_update_text_widget_default() def thread_update_text_widget_default(self): ud = threading.Thread(name="Updating Text Widget from file", target=self.update_text_widget_default, args=()) ud.daemon = False ud.start() def update_text_widget_default(self): print("Updating Text Widget from file") file = fr'c:/users/{os.getlogin()}/Desktop/Gui-Skeleton/default-tmp.txt' with open(file, 'r') as f: console_text = f.readline() for line in console_text: self.text_window.delete('1.0', tk.END) self.text_window.insert(tk.END, f'>>>{line}') def thread_handler_clr_yes_fn_no(self, host): yn = threading.Thread(target=self.proc_handler_clr_yes_fn_no, args=(host,)) yn.daemon = False yn.start() def proc_handler_clr_yes_fn_no(self, host): startprocs = [] lastprocs = [] proc1 = mp.Process(name="Clear + No Filename + WriteFile", target=testping.clr_yes_fn_no_writefile, args=(host,)) proc1.daemon = True proc2 = mp.Process(name="Clear + No Filename + PrintOutput", target=testping.clr_yes_fn_no_print_output, args=(host,)) proc3 = mp.Process(name="Clear + No Filename + Generate PrintOutput to GUI", target=testping.clr_yes_fn_no_print_to_gui, args=(host,)) proc4 = mp.Process(name="Remove first line + Write new default file", target=testping.delete_default_lines) startprocs.append(proc1) startprocs.append(proc2) startprocs.append(proc3) lastprocs.append(proc4) for s in startprocs: s.start() for s2 in startprocs: s2.join() for l in lastprocs: l.start()this is on testping.py: def clr_yes_fn_no_print_to_gui(host, file=sys.stdout): print(f'Current Proccess: {mp.current_process().name} + {mp.current_process().pid}\n') cmd = ["ping", "-n", "4", f'{host}'] proc = sub.Popen(cmd, stdout=sub.PIPE, shell=True, stderr=sub.STDOUT) for stdout_line in proc.stdout: print(stdout_line.decode(), end='', file=file) proc.stdout.close() return_code = proc.wait() if return_code: raise sub.CalledProcessError(return_code, cmd)as for GIT, i'm now using google drive and a local repo but i'll definetly pivot to GIT soon. RE: How can I define a selfmade module's functio to interact with the main .py variables? - Gribouillis - Jun-07-2020 You cannot use threads that easily to update a widget's contents in tkinter because the widgets must be updated from the thread that is running the mainloop(). I give you below a complete example of what I mean with the after() method. In this example, a "producer thread" is started that reads data in a file and puts this data in a queue at a random speed.At the other end of the queue, there is a generator that extracts items from the queue in a non blocking way (so possiby no items at all) and updates the text widget with the data that it receives. This code is run by the main thread. I'm using a trick here: after trying to extract data from the queue, the function runs the "yield" statement, thus becoming a generator. The GUI will use my custom PeriodicConsumer class to drive this generator by intervals of 500 milliseconds. This is done with Tkinter's after() method.It is a complete example, so you can run it and try to understand it. import threading class Error(RuntimeError): pass class PeriodicConsumer: def __init__(self, widget): self.master = widget self._job = None self._stopped = threading.Event() self._stopped.set() self._seq = None def start(self, sequence, millisecs): self.ms = millisecs if self._stopped.is_set(): self._stopped.clear() self._seq = sequence self._job = self.master.after(self.ms, func=self._step) else: raise Error('Expected AfterThread in stopped state') def stop(self): if self._stopped.is_set(): return self._stopped.set() if self._job and ( threading.current_thread() is threading.main_thread()): self.master.after_cancel(self._job) self._job = None def _step(self): if self._stopped.is_set(): return try: next(self._seq) except StopIteration: self.stop() else: self._job = self.master.after(self.ms, func=self._step) if __name__ == '__main__': import io import queue import random import time import tkinter as tk root = tk.Tk() text = tk.Text(root) text.pack() pc = PeriodicConsumer(root) def update_text_from_queue(aqueue): while True: result = [] try: while True: x = aqueue.get_nowait() if x is None: # None in the queue is our signal for # end of data. if result: text.insert(tk.END, "".join(result)) return else: result.append(x) except queue.Empty: pass if result: text.insert(tk.END, "".join(result)) yield myqueue = queue.Queue() pc.start(update_text_from_queue(myqueue), 500) source_file = io.StringIO("""\ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin commodo turpis ex, commodo pretium nunc tincidunt vel. Maecenas quis mi ac ligula sagittis placerat ut ac justo. Fusce nec sodales tortor. Duis hendrerit leo at odio faucibus. """) def produce(aqueue): for line in source_file: aqueue.put(line) time.sleep(random.random()) aqueue.put(None) # our convention for end of input producer = threading.Thread(target=produce, args=(myqueue,)) producer.start() root.mainloop() RE: How can I define a selfmade module's functio to interact with the main .py variables? - Gilush - Jun-08-2020 Ok this is too complicated for my current level i'm afraid. I compermised on just reading from the complete .txt file that contains the output and display it in the text widget when its done but I got lost trying to refresh with the after() func. what it does is actually freeze the textbox (and the program) for the amount of ms I set... how can I make the text window read the file after every time I click submit? self.text_widget() def text_widget(self): self.text_file = open(fr'c:/users/{os.getlogin()}/Desktop/default.txt', 'w+').read() self.text_window = tk.Text(self, height=100, width=500, bg="gray", wrap=tk.WORD) self.text_window.update() self.text_window.grid(row=7, column=1, rowspan=3, sticky="we") self.text_window.insert(tk.END, self.read_file) self.text_window.after(5000, self.read_file) def read_file(self): self.text_file = open(fr'c:/users/{os.getlogin()}/Desktop/default.txt', 'w+').read() self.text_window.insert(tk.END, self.text_file) RE: How can I define a selfmade module's functio to interact with the main .py variables? - Gilush - Jun-08-2020 So i dropped that real time idea for now (too advanced or messy for my current level) and decided to sattle on updating the textbox from file. here's my solution: These are the widget and the reader: def text_widget(self): self.text_window = tk.Text(self, height=100, width=500, bg="gray", wrap=tk.WORD) self.text_window.update() self.text_window.grid(row=7, column=1, rowspan=3, sticky="we") self.text_window.insert(tk.END, ">>") def read_file(self): time.sleep(4) with open(fr'c:/users/{os.getlogin()}/Desktop/default.txt', 'r', encoding='utf-8') as f: self.text_window.insert(tk.END, f.read()) f.close()and this is the handler: def proc_handler_clr_yes_fn_no(self, host): self.text_window.insert(tk.END, ">>Working...") startprocs = [] nextprocs = [] lastprocs = [] proc1 = mp.Process(name="Clear + No Filename + WriteFile", target=testping.clr_yes_fn_no_writefile, args=(host,)) proc3 = mp.Process(name="Read from file to TextBox", target=self.read_file) proc4 = mp.Process(name="Remove first line + Write new default file", target=testping.delete_default_lines) startprocs.append(proc1) nextprocs.append(proc3) lastprocs.append(proc4) for s in startprocs: s.start() for s2 in startprocs: s2.join() t = threading.Thread(name="Display to TextBox", target=self.read_file) t.start() for l in lastprocs: l.start() RE: How can I define a selfmade module's functio to interact with the main .py variables? - Gribouillis - Jun-08-2020 I don't know how it works for you, but from my point of vue, it is not correct to launch a different thread which updates the text window as you are doing with the read_file method. Such things are not normally supported by tkinter. If you want to wait 4 seconds before reading the file, instead of using a thread, simply do self.after(4000, self.read_file)It won't freeze the GUI (but remove the time.sleep() from read_file) Have you tried running the code I posted above? RE: How can I define a selfmade module's functio to interact with the main .py variables? - Gilush - Jun-08-2020 Man I must thank you! you made me break my head with that listener thread and now I got it working!! (still shocked about it lol) I gave up on trying to read the std and just merged the files into one and did some re-design. now I got 3 threads running with 1 communicating with the widget. here it is: def thread_handler(self, host): wt = threading.Thread(target=self.write_to_file, args=(host,)) pt = threading.Thread(target=self.print_to_box, args=(host,)) dt = threading.Thread(target=self.delete_default_lines, args=()) wt.start() pt.start() dt.start()also, I started the entire GUI with a thread and kept it alive: if __name__ == "__main__": lock = threading.Lock() lock.acquire() t = threading.Thread(name="Main GUI", target=main, args=()) t.start()and this is the widget update: def print_to_box(self, host): print(f'Current Proccess: {mp.current_process().name} + {mp.current_process().pid}\n') with sub.Popen(["ping", "-n", '4', f'{host}'], stdout=sub.PIPE, bufsize=1, universal_newlines=True, stderr=sub.STDOUT) as p: for line in p.stdout: self.text_window.insert(tk.END, line)that did the entire trick. Thanks again! |