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

david.giesen at kodak.com david.giesen at kodak.com
Fri Nov 7 16:50:20 CET 2008


Ahh - thanks for  the clear explanations and the workaround!

Do you see these behaviors as "unexpected" enough that I should submit 
them to someone as something to possibly change in the future, or do you 
feel that they are appropriate - or at least appropriate enough not to 
make a change that might break old code?

And if they should be submitted, to whom?

Thanks again!

Dave


"Guilherme Polo" <ggpolo at gmail.com> wrote on 11/07/2008 10:32:06 AM:

> 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
> means:
> 
> 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
> function.
> 
> > 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