Tkinter: How to bind an event that repeats if continued to be selected

Greg Goodman chironsw at swbell.net
Thu Jan 10 16:14:02 EST 2002


> All that needs to be added are a few judicious calls to after() and a
> flag. I'll append a slightly-modified version of your code.

Matt's implementation has a small timing problem.  You can press the
arrow button (set a timer), release the arrow button, and press it again
within 750 ms (setting a new timer before the old one has elapsed).  If
you then hold the button down long enough for both timers to elapse, you
get two updates.  It's not a big problem, but can produce unpredictable
results for a user rapidly single-clicking the button.

My preferred method is to cancel the timer on every button release. 
This ensures that no timer event survives the click-release sequence
that spawned it.  (It also has the admittedly minor benefit of
dispensing with the 'mouse-down' flag, although we must now save the
timer in order to be able to cancel it.)

I'm not sure if it's possible to release the button and find no timer
extant (i.e. button release is processed after one timer elapses, but
before the next timer is created)... It's probably worth putting the
after_cancel() inside a try/except clause, just in case.

Regards,
Greg

--- code ---

import Pmw, Tkinter

class Counter(Pmw.MegaWidget):

    initDelay=1000
    repeatDelay=250

    def __init__(self, parent=None):
        Pmw.MegaWidget.__init__(self, parent)
        self.timer = None
        interior = self.interior()
        self.var = Tkinter.IntVar()
        self.createcomponent(
            'entry', (), None, Tkinter.Entry, (interior,),
            state='disabled', textvariable=self.var,
            width=5).pack(side='left')
        self.arrow = self.createcomponent(
            'arrow', (), None, Tkinter.Canvas, (interior,), width=16,
            height=16, relief='raised', borderwidth=1)
        self.arrow.pack()
        Pmw.drawarrow(self.arrow, 'black', 'up', 'arrow')
        self.arrow.bind('<1>', self.click)
        self.arrow.bind('<ButtonRelease-1>', self.release)

    def click(self,event):
        print "click"
        self.increment()
        self.timer = self.arrow.after(self.initDelay, self.autoIncr)

    def increment(self):
        self.arrow.config(relief='sunken')
        self.var.set(self.var.get()+1)

    def release(self, event):
        self.arrow.after_cancel(self.timer)
        self.arrow.config(relief='raised')

    def autoIncr(self):
        self.increment()
        self.timer = self.arrow.after(self.repeatDelay, self.autoIncr)

if __name__ == "__main__":
  root=Tkinter.Tk()
  Pmw.initialise(root)
  c=Counter(root)
  c.pack()
  root.mainloop()



More information about the Python-list mailing list