views:

23

answers:

2

I'm writing my first GUI program with Tkinter (first program in Python too, actually).

I have an Entry widget for searching, and the results go to a Listbox. I want the results to update as the user types, so I made a callback like this:

search_field.bind("<KeyRelease>", update_results)

The problem is that updates the search many times in a row. Since the results will be coming from a database query, that generates a lot of unnecessary traffic. What I really want is for it to update every second or so, or to wait a second after the user stops typing and then search. What's the easiest way to do that? Thanks

UPDATE: That works great for what I described, but now I've realized that I also need to trigger an update after the user stops typing. Otherwise, the last few characters are never included in the search. I think I have to un-accept the answer in order for this to go back into the list of questions...

+1  A: 

A nice way to do this is a simple caching decorator:

import time
def limit_rate( delay=1.0 ):
    """ produces a decorator that will call a function only once per `delay` """
    def wrapper( func ): # the actual decorator
        cache = dict( next = 0 ) # cache the result and time
        def limited( *args, **kwargs):
            if time.time() > cache['next']: # is it time to call again
                cache['result'] = func( *args, **kwargs) # do the function
                cache['next'] = time.time() + delay # dont call before this time
            return cache['result']
        return limited
    return wrapper

It works like this:

@limit_rate(1.5)
def test():
    print "Called test()"
    time.sleep( 1 )
    return int(time.time())

print [test() for _ in range(5)] # test is called just once

You would simply add this decorator somewhere and decorate your update_results function with it.

THC4k
A: 

Figured it out. I call the decorated function with a delay using any_widget.after(delay_in_ms, function).

Jeff