Python,tkinter:异步修改GUI的正确方法?

时间:2015-02-16 23:14:04

标签: python multithreading user-interface asynchronous tkinter

这是我关于SO的第一个问题。我正在尝试解决UI问题,并且(我认为)有一些具体的问题。为了简化事情并澄清我的推理,让我描述一个例子,同时省略应用程序的不相关部分,我想解决这个问题:

假设我们有一个非常小的Python应用程序,它使用tkinter作为GUI。它所做的就是,如果用户点击“创建文件”按钮并且文件不存在,它会询问用户是否应该使用messagebox.askyesno()创建文件。文件处理由一个单独的FileHandler()类完成,该类没有任何UI的概念,并使用Python队列进行异步通信。这是为了使UI易于互换,我希望保持这一点。

我们的tk主根查询输出队列并将消息框的结果放入输入队列。但这不能使用线程,因为影响GUI的方法只能从主循环中调用。

所以我最后使用Widget.after()轮询输出队列,并在单独的线程中执行文件handlig,以避免死锁。这有效,但这被认为是一个很好的解决方案吗?我怀疑是因为

  1. 似乎不必要地耗费CPU时间(根据top,使用50ms间隔,我的设置不断大约1%)
  2. 有点反应迟钝。在最坏的情况下,用户必须等待整个间隔时间,直到出现消息框。缩短间隔并增加CPU开销
  3. 基本上我想异步创建消息框而不需要轮询的开销。是不是有更好的解决方案可以避免它?

    这是我的示例代码(使用Python 3.4.2测试):

    main.py

    from tkinter import *
    from tkinter import messagebox
    from filehandler import FileHandler
    
    class MyGUI():
    
        poll_id = None
        fh = None
    
        def poll(self):
                self.poll_cancel()
                try:
                    notification = self.fh.outqueue.get_nowait()
                except:
                    self.poll_id = self.root.after(50, self.poll)
                    return
                if notification == 1:
                    yesno = messagebox.askyesno("Question",
                    "Do you want to create the file?")
                    self.fh.inqueue.put(yesno)
                self.fh.outqueue.task_done()
                self.poll_id = self.root.after(50, self.poll)
    
        def poll_cancel(self):
            if self.poll_id is not None:
                self.root.after_cancel(self.poll_id)
                self.poll_id = None
    
        def create_file(self):
            self.fh.create_file()
    
        def __init__(self, fh):
            self.fh = fh
            self.root = Tk()
            self.root.btn = Button(self.root, text="Open file",
            command=self.create_file)
            self.root.btn.pack()
    
            self.poll()
    
            self.root.mainloop()
    
    if __name__ == "__main__":
        MyGUI(FileHandler())
    

    filehandler.py

    import os
    import queue
    import threading
    
    class FileHandler():        
    
        inqueue = None
        outqueue = None
    
        def __init__(self):
            self.inqueue = queue.Queue()
            self.outqueue = queue.Queue()
    
        def create_file(self):
            threading.Thread(target=self.create_file_thread).start()
    
        def create_file_thread(self):
            if not os.path.exists("file"):
                self.outqueue.put(1)
                yesno = self.inqueue.get()
                if yesno == 1:
                    try:
                        with open("file", 'a'):
                            os.utime("file")
                    except Exception as e:
                        print(e)
                self.inqueue.task_done()
    

0 个答案:

没有答案