1

I have a set of entry boxes created in a for loop and recorded in a dictionary that I want to validate independently. The loop that creates them follows:

vcmd = (self.register(self.VE), '%P')
for pos in [(i,j) for i in range(9) for j in range(9)]:
            self.inputDict[pos] = tk.StringVar()
            self.entryDict[pos] = tk.Entry(font = ('Arial', 20, 'bold'),
                                           textvariable = self.inputDict[pos],
                                           borderwidth = 1, width = 2,
                                           justify = 'center',
                                           validatecommand = vcmd,
                                           validate = 'key')

And the code for self.VE is here:

def VE(self, P, pos):
    if P in [str(i) for i in map(chr,range(49,58))]:
        self.solution.insertVal(P,pos)
    elif P == '': self.solution.removeVal(pos)
    return P in [str(i) for i in map(chr,range(49,58))]+['']

My issue is that I cannot figure out how to get VE to take arguments that are not included in the list provided by this answer, duplicated below:

# valid percent substitutions (from the Tk entry man page)
# %d = Type of action (1=insert, 0=delete, -1 for others)
# %i = index of char string to be inserted/deleted, or -1
# %P = value of the entry if the edit is allowed
# %s = value of entry prior to editing
# %S = the text string being inserted or deleted, if any
# %v = the type of validation that is currently set
# %V = the type of validation that triggered the callback
#      (key, focusin, focusout, forced)
# %W = the tk name of the widget

I believe I'll need to make my change to the line defining vcmd, but I don't know what change to make to allow the validation command to take the position of the entry (that is, the value of pos) as well as the attempted input. How do I add an argument to the validation command that isn't in that list?

Community
  • 1
  • 1
user2569332
  • 555
  • 1
  • 4
  • 12

1 Answers1

2

The value you give to the validatecommand is a tuple of the registered command and any arguments you want to pass to the command. Therefore, you can do something like this:

    vcmd = self.register(self.VE)

    for pos in ...:
        self.entryDict[pos] = tk.Entry(..., 
                                       validatecommand=(vcmd, "%P", pos), 
                                       ...)
    ...

def VE(self, P, pos):
    print "P: %s pos: %s" % (P, pos)
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • This is great. Thank you. May I ask why the command must be registered before it can be set as the validatecommand function? At first I thought it might be to make it a valid callback, but lambda functions work just as well, at least for things that don't take special arguments. Also, is formatting in the '%P' string representative of the new input? I don't understand how strings can be valid argument keywords. I thought keywords had to be variable names. – user2569332 Aug 14 '13 at 18:21
  • Now this is weird. After adjusting my code, I tried your answer and came up with a call to pos in VE that returned a string, not the tuple value that pos takes in self.entryDict.keys(). It's not a problem to extract the data from the pos string to reform the pos tuple, but what coerces it to a string? It doesn't even return the same string as str(pos). – user2569332 Aug 14 '13 at 18:47
  • This data is being passed to an underlying Tcl interpreter, which has no knowledge of python datatypes. In Tcl "everything is a string", so the translation is happening at the python/Tcl boundary. – Bryan Oakley Aug 14 '13 at 20:30