tkinter避免GUI冻结

时间:2017-11-20 11:48:18

标签: python-2.7 tkinter python-multithreading

我开发了一个简单的Python应用程序,然后我决定使用Tkinter添加一个简单的GUI。

问题是,当我调用一个名为startprocess的函数并开始执行处理器繁重且窗口冻结的东西时。

我知道这是一个常见的问题,我已经读过我应该使用多线程(非常复杂,因为该函数也会更新GUI)或者将我的代码分成不同的函数,每个函数都运行一段时间。无论如何,下面的代码需要进行任何修改以避免GUI冻结吗?

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()

1 个答案:

答案 0 :(得分:0)

你以错误的方式使用线程。

首先: target=需要没有()和参数的函数名称。
您可以为args=分配参数(即使您只有一个参数,也必须为tuple

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

现在你的代码作为普通函数运行parselog_v1.main,等待结果并将结果作为函数名称分配给taget= - 所以你有这样的东西:

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

它会停止mainloop,因此它无法获取鼠标/键盘事件,刷新窗口等等,因此它看起来像窗口冻结。

第二个:正确创建线程后你必须启动它

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