如何在运行循环/函数时停止GUI冻结?

时间:2018-01-02 20:41:50

标签: python multithreading tkinter

我正在尝试将线程添加到Python 3.63 Tkinter程序中,其中一个函数将运行,但GUI仍然会响应,包括用户是否想在函数运行时关闭程序。

在下面的示例中,我尝试在GUI主循环的单独线程上运行简单的打印到控制台功能,这样用户可以在循环运行时单击右上角的X来关闭程序,如果他们愿意的话

我得到的错误是:

TypeError: start() takes 1 positional argument but 2 were given

try:
    import tkinter as tk
    import queue as queue
except:
    import Tkinter as tk
    import Queue as queue
import threading

def center(toplevel,desired_width=None,desired_height=None):
    toplevel.update_idletasks()
    w, h = toplevel.winfo_screenwidth() - 20, toplevel.winfo_screenheight() - 100
    if desired_width and desired_height:
        size = (desired_width,desired_height)
    else:
        size = tuple(int(Q) for Q in toplevel.geometry().split("+")[0].split("x"))
    toplevel.geometry("%dx%d+%d+%d" % (size + (w/2 - size[0]/2, h/2 - size[1]/2)))

class ThreadedTask(threading.Thread):
    def __init__(self,queue):
        threading.Thread.__init__(self)
        self.queue = queue
    def run(self,func):
        func()

class app(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        center(self,desired_width=500,desired_height=400)
        self.queue = queue.Queue()
        self.run_func_button = tk.Button(self,
                                         text="Run Function",
                                         font=("Calibri",20,"bold"),
                                         command=self.run_func)
        self.run_func_button.pack()

    def run_func(self):
        ThreadedTask(self.queue).start(self.count_to_1500)

    def count_to_1500(self):
        for i in range(1500):
            print (i)

app_start = app()
app_start.mainloop()

2 个答案:

答案 0 :(得分:2)

请参阅文档threading - start()不使用参数,但使用.start(self.count_to_1500) - 这会给出错误。

您可以使用

Thread(target=self.count_to_1500).start()

Thread(target=self.count_to_1500, args=(self.queue,)).start()

如果你定义

def count_to_1500(self, queue):

编辑:使用放入quoue的线程和从队列中获取数据的方法的工作示例。

try:
    import tkinter as tk
    import queue as queue
except:
    import Tkinter as tk
    import Queue as queue
import threading
import time

def center(toplevel,desired_width=None,desired_height=None):
    toplevel.update_idletasks()
    w, h = toplevel.winfo_screenwidth() - 20, toplevel.winfo_screenheight() - 100
    if desired_width and desired_height:
        size = (desired_width,desired_height)
    else:
        size = tuple(int(Q) for Q in toplevel.geometry().split("+")[0].split("x"))
    toplevel.geometry("%dx%d+%d+%d" % (size + (w/2 - size[0]/2, h/2 - size[1]/2)))

class app(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        center(self,desired_width=500,desired_height=400)
        self.queue = queue.Queue()
        self.run_func_button = tk.Button(self,
                                         text="Run Function",
                                         font=("Calibri",20,"bold"),
                                         command=self.run_func)
        self.run_func_button.pack()

    def run_func(self):
        threading.Thread(target=self.count_to_1500).start()
        threading.Thread(target=self.count_to_1500_with_queue, args=(self.queue,)).start()
        self.check_queue()

    def count_to_1500(self):
        for i in range(10):
            print('1:', i)
            time.sleep(0.2)

    def count_to_1500_with_queue(self, queue):
        for i in range(10):
            print('put:', i)
            queue.put(i)
            time.sleep(1)
        queue.put('last')

    def check_queue(self):
        print("check queue")
        data = None
        if not self.queue.empty():
            data = self.queue.get()
            print('get:', data)
        if data != 'last':
            self.after(200, self.check_queue)

app_start = app()
app_start.mainloop()

答案 1 :(得分:1)

Thread.start不带参数:https://docs.python.org/3/library/threading.html

使用Thread的正确方法是:

# Will call func(*args, **kwargs)
t = threading.Thread(target=func, args=(), kwargs={})
t.start()

t.join()

加入很重要。没有它,你的应用程序中会有很多僵尸线程,这也会阻止你的应用程序干净利落地关闭。

另一种模式是使用守护程序线程来处理队列。程序退出时会自动终止守护程序线程。

def worker(q):
    while True:
      try:
        f = q.get()
        q.task_done()
        if f is None: return
        f()
      except Exception:
        import traceback
        traceback.print_exc()

q = Queue.Queue()    
t = threading.Thread(target=worker, args=(q,))
t.daemon=True
t.start()


# f is a no-arg function to be executed
q.put(f) 


# Call at shutdown
q.join()

要同时运行多个任务,请启动多个线程。

又一种方法,使用multiprocessing.pool.ThreadPool

from multiprocessing.pool import ThreadPool

# Create at startup
pool = ThreadPool(8)

# For each treaded task
pool.apply_async(func, args, kwds) 

# Call at shutdown
pool.close()
pool.join()

......或多或少有效,如上所述。

我建议阅读:

https://docs.python.org/2/library/multiprocessing.html#multiprocessing-programming