Tkinter: How to use threads to preventing main event loop from “freezing”

后端 未结 4 942
囚心锁ツ
囚心锁ツ 2020-11-21 23:25

I have a small GUI test with a \"Start\" button and a Progress bar. The desired behavior is:

  • Click Start
  • Progressbar oscillates for 5 seconds
4条回答
  •  刺人心
    刺人心 (楼主)
    2020-11-22 00:20

    I have used RxPY which has some nice threading functions to solve this in a fairly clean manner. No queues, and I have provided a function that runs on the main thread after completion of the background thread. Here is a working example:

    import rx
    from rx.scheduler import ThreadPoolScheduler
    import time
    import tkinter as tk
    
    class UI:
       def __init__(self):
          self.root = tk.Tk()
          self.pool_scheduler = ThreadPoolScheduler(1) # thread pool with 1 worker thread
          self.button = tk.Button(text="Do Task", command=self.do_task).pack()
    
       def do_task(self):
          rx.empty().subscribe(
             on_completed=self.long_running_task, 
             scheduler=self.pool_scheduler
          )
    
       def long_running_task(self):
          # your long running task here... eg:
          time.sleep(3)
          # if you want a callback on the main thread:
          self.root.after(5, self.on_task_complete)
    
       def on_task_complete(self):
           pass # runs on main thread
    
    if __name__ == "__main__":
        ui = UI()
        ui.root.mainloop()
    

    Another way to use this construct which might be cleaner (depending on preference):

    tk.Button(text="Do Task", command=self.button_clicked).pack()
    
    ...
    
    def button_clicked(self):
    
       def do_task(_):
          time.sleep(3) # runs on background thread
                 
       def on_task_done():
          pass # runs on main thread
    
       rx.just(1).subscribe(
          on_next=do_task, 
          on_completed=lambda: self.root.after(5, on_task_done), 
          scheduler=self.pool_scheduler
       )
    

提交回复
热议问题