Romby Soft
Romby Soft

Reputation: 13

tkinter avoid GUI from freezing

I developed a simple Python application doing some stuff, then I decided to add a simple GUI using Tkinter.

The problem is that, while the I call a function called startprocess and begin doing stuff which is processor heavy and the window freezes.

I know it's a common problem and I've already read that I should use multithreads (very complicated, because the function updates the GUI too) or divide my code in different function, each one working for a little time. anyways is there any modification needed in below code to avoid GUI freezing?

import threading
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import os, datetime, sys, subprocess
import parselog_v1


# diplay messagebox window
def MessageBox(windowLable,msg):
   messagebox.showinfo(windowLable, msg)


# check if Dir empty
def checkDirEmpty(work_path):
    if os.path.isdir(work_path):
        if not os.listdir(work_path):
            print ("No Files found in directory")
            MessageBox('Log Parser', 'No Files found in directory.')
        else:
            return True



# launch app in center of screen
def center_window(width=300, height=200):
    # get screen width and height
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight()
    # calculate position x and y coordinates
    x = (screen_width/2) - (width/2)
    y = (screen_height/2) - (height/2)
    root.geometry('%dx%d+%d+%d' % (width, height, x, y))

# application frame

class Application(tk.Frame):

    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
        self.pack()
        self.createWidgets()
        self.master.title("Log Parser")

    def createWidgets(self):
        self.Run_Main = tk.Button(self)
        self.Run_Main["text"] = "Browse for logs"
        self.Run_Main["fg"] = "blue"
        self.Run_Main["command"] = self.startProcess
        self.Run_Main.pack(side='left',padx=0)

        self.QUIT = tk.Button(self)
        self.QUIT["text"] = "Quit!"
        self.QUIT["fg"]   = "red"
        self.QUIT["command"] =  self.quit
        self.QUIT.pack(side='right',padx=5)

    def startProcess(self):
        global Src_foldername
        Src_foldername = filedialog.askdirectory()
        Src_foldername = Src_foldername.replace("/", "\\")
        print("Source folder: " + Src_foldername)
        if checkDirEmpty(Src_foldername):
            # process logs
            # multithread
            print("Processing...")
            self.refresh()
            threading.Thread(target=parselog_v1.main(Src_foldername))



# scroll text inside application frame
class scrollTxtArea:

    def __init__(self, root):
        frame = tk.Frame(root)
        frame.pack()
        self.textPad(frame)
        return

    class IORedirector(object):
        '''A general class for redirecting I/O to this Text widget.'''
        def __init__(self, text_area):
            self.text_area = text_area

    class StdoutRedirector(IORedirector):
        '''A class for redirecting stdout to this Text widget.'''



    def textPad(self, frame):
        # add a frame and put a text area into it
        textPad = tk.Frame(frame)
        self.text = tk.Text(textPad, height=21, width=68)
        self.text.config()
        # add a vertical scroll bar to the text area
        scroll = tk.Scrollbar(textPad)
        self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
        # pack everything
        self.text.pack(side=tk.LEFT, pady=2)
        scroll.pack(side=tk.RIGHT, fill=tk.Y)
        textPad.pack(side=tk.TOP)
        self.text.insert("end", "Begin by selecting log folder..." + "\n")
        self.text.configure(state='disabled') # disable text editing
        sys.stdout = (self) # to begin logging stdio to GUI
        return

    def write(self, txt):
        self.text.configure(state='normal')
        self.text.insert('end', txt)
        self.text.configure(state='disabled')

root = tk.Tk()
root.resizable(width=False, height=False)
center_window(500, 300) # launch in center of screen
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
root.destroy()

Upvotes: 0

Views: 1663

Answers (1)

furas
furas

Reputation: 142631

You use thread in wrong way.

First: target= needs function name without () and arguments.
You can assign arguments to args= (it have to be tuple even if you have only one argument)

threading.Thread(target=parselog_v1.main, args=(Src_foldername,) )

Now your code runs parselog_v1.main as normal function, waits for result and it will assign this result as function name to taget= - so you have something like this:

result = parselog_v1.main(Src_foldername)
threading.Thread(target=result)

It stops mainloop so it can't get mouse/keyboard events, refresh window, etc. so it looks like window freeze.

Second: after you create thread correctly you have to start it

my_thread = threading.Thread(target=parselog_v1.main, args=(Src_foldername,) )
my_thread.start()

Upvotes: 1

Related Questions