i have created a tkinter gui that does some time consuming operations when clicking on some buttons. There are 2 main problems in my case that i think cause tkinter to crash in an unconssistent way.
1) I want to log some output from the new threads into a scrolled text widget that runs on the main thread, which i Think is not a good idea. Right now i do that by having a TextHandler object send to the threads (see my code)
2) In order to avoid logging from the new thread to the main thread, i want to know when the thread is done and then log from the main thread into the text widget but how does the main loop knows, when the thread is created in the function called from the Button press?
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(tkinter.END, msg + '\n')
self.text.configure(state=DISABLED)
# Autoscroll to the bottom
self.text.yview(tkinter.END)
# This is necessary because we can't modify the Text from other threads
self.text.after(0, append)
class GUI(Frame):
def __init__(self, parent, logger, st):
....initialize stuff...
def _startComputations(self):
if (self._paths == ""):
self._logger.warn("No drive paths found, please add a path")
else:
if (self._flexinput == ""):
self._logger.warn(self._flexinput)
self._logger.warn("No file path,continuing with standard KM estimation")
self.myThread = StandardUsecases(self._paths, self._input,
self._logger, self._standard_choices,
self._numberOfoccurences_usecases,
self._all_logs, self._unique_countries,
self.bt, self.bt2, self.bt3,
self._flexinput)
self.myThread.start()
self._all_jsons = self.myThread._original_json_list
self._general_json = self.myThread._general_json_data_list
def _KM_Button(self):
self._all_logs[:] = []
self.bt = Button(self.frame6, text='1-Main Usecases', font = "Helvetica 11 bold italic",
command = self._startComputations,relief = RAISED, bd= 6, bg = "pale green", fg = 'black')
self.bt.pack(side = LEFT)
def initGUI(self):
self.parent.title("DC Statistics calculator")
self.pack(fill=BOTH, expand=True)
self._Drive_Paths()
self._Flexray_Path()
#self._UserInput()
self._KM_Button()
self._countButton()
self._exportButton()
self._helpButton()
def main():
root = Tk()
root.geometry("700x800+400+400")
st = scrolledtext.ScrolledText(root, state='disabled')
st.configure(font="Times 13")
st.pack(side=BOTTOM, fill='both',expand='yes')
text_handler = TextHandler(st)
logger = logging.getLogger()
logger.addHandler(text_handler)
app = GUI(root, logger, st)
root.mainloop()
if __name__ == '__main__':
main()
So after pressing the button, the _startComputations function will be called, and there the thread will be created. I send the _logger object so that i can log to the ScrolledText widget while i am on the new thread. But most of the times i get crashes like "python.exe stopped working" or tcl_appendlimitedtoobj called with shared object.
In the case i do not want to log from the new thread, how can the main loop know if the new thread is done, since the new thread is created withid the function called after the button press?
Thank you