glib.idle_add for tkinter in python

After doing a lot of python GTK+ work on Exaile, I’ve found that it uses glib.idle_add() extensively — and usually with good reason. idle_add is great if you want to ensure that whatever you’re calling is being called on the GUI thread, so that way you don’t have to worry too much about thread interactions as long as you keep things separate.

Another mentor and I are developing a GUI video game along with a ‘fake wpilib’ for our FIRST Robotics programming students to help them learn how to program, and as such we’ve decided to use TKinter for the GUI toolkit (since it supports Python 3, and usually doesn’t require the kids to install anything special to make it work). However, as I started making things I couldn’t find the equivalent of idle_add() for TKinter, and I guess there isn’t one — after_idle() apparently blocks until the event loop is idle, and so that isn’t what I wanted.

A number of posts I found online advocated to poll a queue for input… but I *really* dislike polling, and try to avoid it when I can. So I wrote up this set of routines that is roughly equivalent to idle_add() in tkinter, and uses a queue while avoiding polling.

        
import Tkinter as tk
from queue import Queue, Empty

def idle_add(callable, *args):
    '''Call this with a function and optional arguments, and that function
       will be called on the GUI thread via an event.
       
       This function returns immediately.
    '''
    queue.put((callable, args))
    root.event_generate('<<Idle>>', when='tail')
    
def _on_idle(event):
    '''This should never be called directly, it is called via an 
       event, and should always be on the GUI thread
    '''
    while True:
        try:
            callable, args = queue.get(block=False)
        except queue.Empty:
            break
        callable(*args)
        
queue = Queue()
root = tk.Tk()
root.bind('<<Idle>>', _on_idle)

Leave a Reply