[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