[Tkinter-discuss] Scale widget fires command on entering mainloop?

Guilherme Polo ggpolo at gmail.com
Fri Nov 7 16:32:06 CET 2008

On Fri, Nov 7, 2008 at 12:53 PM,  <david.giesen at kodak.com> wrote:
> "Guilherme Polo" <ggpolo at gmail.com> wrote on 11/07/2008 09:23:48 AM:
>> Comparing this Scale example with a command set and a Button with a
>> command set is unfair. When you change Scale's value, the scale
>> eventually has to be redraw.
>> So, you create a Scale with the "command" option set, then you change
>> its value by calling scale.set, then you run mainloop. When mainloop
>> runs the scheduled events start firing, and there is one around there
>> saying your scale changed its value so the slider has to be redrawn.
>> But you also set a command, so, since the scale changed now it must be
>> invoked, and the callback is called.
>> Can you explain why is it a problem to invoke the callback if the
>> scale has changed ? And why don't you want it to be called the first
>> time.
>> --
>> -- Guilherme H. Polo Goncalves
> Thanks for the quick response and the explanation about there being a
> scheduled events queue that waiting for the mainloop to start, Guilherme.
> That's something I hadn't thought about and it explains why there is a
> delayed command firing.  However, it doesn't change my thinking that the
> command shouldn't be firing at all in these two cases.
> First off, I have no problem if the callback is invoked when the scale is
> changed, that is why I'm using it.
> However, in the first case, I don't think of the scale being 'changed'.
> I'm giving it the initial value via the Tkinter variable scalevar and I'm
> not changing it, so I don't understand why the command gets called.  The
> scale doesn't have to be redrawn, only drawn the first time.  Maybe my
> original button argument wasn't the best, but I do see this as the same as
> a Checkbutton.  I can pass in an initial value to the Checkbutton via a
> Tkinter variable, and the Checkbutton command does not fire when the
> mainloop starts (see the example below, which I've also fixed so it no
> longer imports a custom module of my own - it should run for anyone now).
> And in the second case, there is no command callback registered at the
> time that I change the scale value - the callback is added later via a
> configure statement.  So again, I don't see why a callback is registered
> for the change.  This behavior is again different from a checkbutton - if
> I create a checkbutton with no callback, then change the value, then add a
> callback function, the callback command is not called.  I've added both
> the checkbutton examples to the code below for comparison.
> As for why I don't want the callback to be called when the widget is first
> created, well I might have something in my callback routine that I only
> want to occur in response to the user changing the initial scale value.
> Regardless, it seems like quite non-standard and unexpected (to me)
> behavior since other widgets don't do this.
> Dave
> import Tkinter as Tk
> root = Tk.Tk()
> def printme1(value):
>        print 'Scale # 1 just fired with value:', value
> scalevar = Tk.IntVar()
> scalevar.set(7)
> scale1 = Tk.Scale(root, command=printme1, variable=scalevar)
> print 'Scale #1 initialized with value:', scale1.get()

Ok, it is understandable that you want the callback to not be invoked
in this case, but unfortunately that is how scale works for now. It
may not make a difference to the problem, but the behaviour of
ttk.Scale is the one you are expecting.

> def printme2(value):
>        print 'Scale # 2 just fired with value:', value
> scale2 = Tk.Scale(root)
> print 'Scale #2 initialized with value: ', scale2.get()
> scale2.set(4)
> print 'Scale #2 just set to value: ', scale2.get()
> scale2.configure(command=printme2)

Now this is basically the same as the example in the other email. It
is differently internally in tkScale.c, but will result in the same
actions. Calling scale2.configure includes setting the scale value to
itself to ensure that the scale's value is within the new acceptable
range for the scale (note that you didn't change its range, but
unfortunately it does this anyway). When it goes to set the scale's
value, the scale command is already set (you just gave it one), so it
is scheduled for redrawn and the callback will eventually be called.

To achieve what you want you will have to drop the "command" option
usage and stick to tcl variables. In the first example in this email
you already used an IntVar, now all you have to do is trace that
variable for changes on it, specially when you write to it. This

scalevar.trace_variable('w', callback)

callback is a function that takes 3 arguments, the first is the
variable name, the second is just used if you are using an tcl array
(Tkinter.py doesn't wrap it), and the third argument will be the flags
given to trace_variable ('w' here).
To access the variable value in the callback you would do something
like: root.globalgetvar(varname) where root could be Tkinter.Tk() for
example, and varname would be the first parameter in the callback

> def printme3():
>        print 'Checkbutton # 1 just fired with value:', cbvar.get()
> cbvar = Tk.IntVar()
> cbvar.set(1)
> cb1 = Tk.Checkbutton(root, variable=cbvar, command=printme3)
> print 'Checkbutton #1 initialized with value: ', cbvar.get()
> def printme4():
>        print 'Checkbutton # 2 just fired with value:', cbvar2.get()
> cbvar2 = Tk.IntVar()
> cbvar2.set(1)
> cb2 = Tk.Checkbutton(root, variable=cbvar)
> cb2.invoke()
> print 'Just invoked cb2 with no callback'
> cb2.configure(command=printme4)
> print 'Now packing scales'
> scale1.pack()
> scale2.pack()
> cb1.pack()
> print 'Done packing scales'
> root.mainloop()

-- Guilherme H. Polo Goncalves

More information about the Tkinter-discuss mailing list