So let's say I have a Tkinter GUI that shows a RUN button, a STOP button and a ScrolledText
widget. Pressing the RUN button will call a function that includes several log.info()
calls. And the text widget is supposed to show those logs. I would like to make those logs showing up real-timely as it occurs, so I tried to run the function as threading, and want it that pressing STOP button would terminate the function. Here is what I have now:
My code:
import tkinter as tk
import logging
import tkinter.scrolledtext as tkst
import threading
class TextHandler(logging.Handler):
"""This class allows you to log to a Tkinter Text or ScrolledText widget"""
def __init__(self, text):
# run the regular Handler __init__
logging.Handler.__init__(self)
# Store a reference to the Text it will log to
self.text = text
def emit(self, record):
msg = self.format(record)
def append():
self.text.configure(state='normal')
self.text.insert(tk.END, msg + '\n')
self.text.configure(state='disabled')
# Autoscroll to the bottom
self.text.yview(tk.END)
# This is necessary because we can't modify the Text from other threads
self.text.after(0, append)
def setup_logger(logger_name, log_file, level=logging.INFO):
l = logging.getLogger(logger_name)
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
if path.exists(log_file) == False:
fileHandler = logging.FileHandler(log_file, mode='w')
else:
fileHandler = logging.FileHandler(log_file, mode='a')
fileHandler.setFormatter(formatter)
streamHandler = logging.StreamHandler()
streamHandler.setFormatter(formatter)
l.setLevel(level)
l.addHandler(fileHandler)
l.addHandler(streamHandler)
setup_logger('logA', 'HeresTheLog.log')
log = logging.getLogger('logA')
def threadding():
app.running = True;
newthread = threading.Thread(target= function)
newthread.start()
def function():
log.info("starts")
(do things)
log.info("ends")
def stop():
app.running = False;
class myApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self, width=768, height=576, bg="")
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.running = True
self.frames = {}
self.show_frame(StartPage)
self.update_idletasks()
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
button2 = ttk.Button(self, text="FUNCTION",
command= function
)
button2.pack(pady=10)
textLog = tkst.ScrolledText(self, state="disabled")
textLog.pack()
text_handler = TextHandler(textLog)
log.addHandler(text_handler)
stopbutton = ttk.Button(self, text = "STOP!", command = stop)
stopbutton.pack()
app = myApp()
app.mainloop()
So basically, I am trying to use app.running
to control the threading, but this doesn't seem to work. And I know that you can't really just kill a threading. So I am wondering what would be the better way to do this? Maybe using a multiprocess rather than threading so that we could kill it to stop it? Could someone please elaborate on that idea?