[Tkinter-discuss] StringVar() .trace(), .trace_vdelete() use

Bob Greschke bob at passcal.nmt.edu
Thu Mar 7 22:56:18 CET 2013


Now I understand where "bloatware" comes from. :)
This will make your eye's water:

###################################
PROG_NAME = "Headache"

from Tkinter import *
PROG_SEARCHMODS = "><=#*~_"
PROGFw = {}
PROGFw["Qty"] = 7
PROGBar = {}

Root = Tk()
ROOTNameVar = StringVar()
ROOTQtyVar = StringVar()
ROOTQtyVar.set("HI")

Clr = {"R":"#FF0000", "D":Button().cget("bg")}



####################
class vEntry(Entry):
    def __init__(self, master=None, bar = "", **kw):
        Entry.__init__(self, master, **kw)
        VCmd = (self.register(self.VCmd), '%s', '%P')
        self.configure(vcmd = VCmd, validate='key')
        self.MaxLen = self.cget("width")-1
        self.icursor(END)
        self.VarSet = bar
    def VCmd(self, Old, New):
        Max = self.MaxLen
        if len(New) > 0:
# If the first character of the field's text is one of the search modifiers
# then allow one more character to be entered.
            if New[0] in PROG_SEARCHMODS:
		Max += 1
	if len(New) > Max:
            Root.bell()
            return False
        if self.VarSet != "":
            PROGBar[self.VarSet].configure(bg = Clr["R"])
            eval("%sBarSVar"%self.VarSet).set(1)
        return True



###########################################
def chgPROGChgBar(VarSet, State, e = None):
    if State == 1:
# This may be called upon to clear the change bar of a form that doesn't have
# a change bar, so try.
        try:
            PROGBar[VarSet].configure(bg = Clr["R"])
            eval("%sBarSVar"%VarSet).set(1)
	except:
            pass
    elif State == 0:
	try:
            PROGBar[VarSet].configure(bg = Clr["D"])
            eval("%sBarSVar"%VarSet).set(0)
	except:
            pass
# For some routines to use so they don't have to constantly check to see if
# the change bar state should be changed. The call gets made, but nothing
# happens.
    elif State == -1:
        return
    return



##################################################
def labelEntry(Sub, LabTx, TheVar, Max, Bar = ""):
    Label(Sub, text = LabTx).pack(side = LEFT)
    if isinstance(Max, basestring) or isinstance(Max, str):
        Max = PROGFw[Max]
    LEnt = vEntry(Sub, textvariable = TheVar, width = Max+1, bar = Bar)
    LEnt.pack(side = LEFT)
    return LEnt



######################
def rootDoSomething():
    print "Working..."
# Clean all input fields (mostly .strip()'ing)
# Verify what was entered (.upper() and/or look for missing info and/or make
# sure an integer is entrered where only an integer can be entered, etc.)
    try:
        int(ROOTQtyVar.get())
    except:
        print "Qty: Must be an integer."
        return
# Save to db, or use inputs to make an SQL command for a search, etc.
    ROOTNameVar.set("")
    ROOTQtyVar.set("")
    chgPROGChgBar("ROOT", 0)
    print "Done."
    return



##################
LFrm = Frame(Root)
ROOTBarSVar = IntVar()
PROGBar["ROOT"] = Frame(LFrm, height = 5)
PROGBar["ROOT"].pack(side = TOP, fill = X, expand = YES, pady = 3)
Sub = Frame(LFrm)
labelEntry(Sub, "Name:", ROOTNameVar, 20)
Sub.pack(side = TOP, pady = 20)
Sub = Frame(LFrm)
labelEntry(Sub, "Qty:", ROOTQtyVar, "Qty", "ROOT")
Sub.pack(side = TOP, padx = 20, pady = 20)
Sub = Frame(LFrm)
Button(Sub, text = "Go", command = rootDoSomething).pack(side = TOP)
Sub.pack(side = TOP)
LFrm.pack(side = TOP)

Root.mainloop()

######################################

It's micro-mini-chunks of code from a 42,000 line inventory and shipping program.

The class does what it needs to do and dovetails in with the rest of the program.  So we're telling a View object, the Entry() (you caught me -- trying to learn MVC stuff in iOS/OSX), to control the contents of a Model object, the StringVar(), instead of having model things, functions via .trace(), control the contents of a Model thing, the StringVar(), which would then automagically update the View.  Right?  Jobs would never approve. He probably wouldn't approve either way.

I might keep it if I can't figure out what happens when you StringVar.trace() on the same variable twice.  Using .trace() and just looking at the contents of the StringVar() seems way more straightforward (read non-obfuscated) to me.  The only thing that didn't make sense was the lambda. :)

Thanks!


On 2013-03-07, at 02:01, Michael Lange <klappnase at web.de> wrote:

> Hi,
> 
> On Wed, 6 Mar 2013 20:11:39 -0700
> Bob Greschke <bob at greschke.com> wrote:
> 
>> I saw that earlier in the day (and a couple of weeks ago too).  I
>> understand it just about as well as lambdas. :)  Do all of those values
>> get passed each time a key is pressed?  
> 
> Yes, when you register a function as validation callback, all the
> registered values will be passed each time a validation occurs (which may
> be every key press when validate='key', focus in/out events if
> validate='focus(in/out)' or all of these if validate='all' .
> 
>> Also, each Entry() field has a
>> different max length.  Where do I pass that in all of this?  It would
>> need to be an arg to vcmd in the Entry().
> 
> This sounds like a job for a custom entry class. I wrote a
> quick example to illustrate the usage of the Entry's vcmd that might be a
> starting point for you:
> 
> #######################################################################
> from Tkinter import *
> 
> class VEntry(Entry):
>    def __init__(self, master=None, maxlength=5, value='foo', **kw):
>        Entry.__init__(self, master, **kw)
> 
>        self._maxlength = maxlength
>        self.insert('end', value)
>        vcmd = (self.register(self._vcmd), '%s', '%P')
>        self.configure(vcmd=vcmd, validate='all')
> 
>    def _vcmd(self, old, new):
>        print old, new
>        if len(new) > self._maxlength:
>            return False
>        return True
> 
> root = Tk()
> e = VEntry(root, maxlength=7, value='bar')
> e.pack(padx=100, pady=100)
> e.focus_set()
> root.mainloop()
> #######################################################################
> 
> You see, the `%s' ("old value") parameter in this example is not really
> needed, I added it just to illustrate how to pass multiple arguments to
> the vcmd. See http://www.tcl.tk/man/tcl8.4/TkCmd/entry.htm#M16 for a list
> of all possible percent substitutions and their meanings.
> You could also try if validate='key' is sufficient for your needs.
> 
> The one thing one has to be very careful to keep in mind when writing a
> validation command callback is that it must always return a boolean value
> in any case, otherwise Tk will be "confused" and simply and silently turn
> off validation altogether. This may especially easily happen when the vcmd
> callback happens to throw an error, so one has to take care that all
> possible exceptions are properly caught by try...except statements.



More information about the Tkinter-discuss mailing list