0

I have carefully reviewed answers to Interactively validating Entry widget content in tkinter, but my script fails to restore previous value if the validate command returns False. I captured %P and %s and print them out...They both show the same value.

import tkinter as tk

class Controller :
    def __init__(self) :
        i=10
        j=20
        # list comprehension
        self.entry_widgets = [[None for col in range(j)] for row in range(i)]
        #print(self.entry_widgets)

        self.values = [["string"+str(row) + str(col) for col in range(10)] for row in range(20)]
        #print(self.values)
class EnterBox(tk.Entry):

    def __init__(self,*args,**kwargs):
        #print (args)
        self._modified = False
        self._save = 0
        self._raise = 1
        self._lower = 2


        frame, i,j, *newargs = args
        self._content = tk.StringVar()

   #        tk.Entry.__init__(self,frame,*newargs,
   #                          validate = 'focusout', 
   #                          validatecommand = vcmd,
   #                          **kwargs)
        tk.Entry.__init__(self,frame,*newargs,**kwargs)

        vcmd = (self.register(self._revert), '%P', '%s')
        ct.entry_widgets[i][j] = self
        self.config(textvariable=self._content)
        self.config(validate = "focusout")
        self.config(validatecommand = vcmd )
        x=(ct.values[i][j])
        self.insert(0,x)
       #self._content.set(x)
        self.bind("<Return>",lambda  event, x=self._save : self._action(event,x) )
        self.bind("<Button-2>",lambda event, x=self._save : self._action(event,x) )
        self.bind("<FocusIn>", lambda event, x=self._raise : self._action(event,x))
        self.bind("<FocusOut>", lambda event, x=self._lower : self._action(event,x))
        self.bind('<Button-3>', lambda event, x=self._lower : self._action(event,x))

        self.grid(column=i+1,row=j+2)

def _revert(self,P,s):
    print ("Hi There")
    print(P)
    print(s)
    return False
def _action(self,event,action):
    print(str(action)+' ' + str(event))
    if action == self._save :
        ct.values[i][j] = self._content.get()
        self.config(bg='lightskyblue2')
        self._modified = True
    elif action == self._raise :
        self.config(bg = 'light pink')
    elif action == self._lower :
        self.config(bg = 'gray80')
        self._modified = False
    else :
        print('action value is bad action =>' + str(action))
if "__main__" == __name__ :
    root = tk.Tk()
    frame = tk.Frame()
    i=j=0

    ct = Controller()
    root.grid()
    frame.grid()
    check = EnterBox(frame,i,j,width = 24)
    check2 = EnterBox(frame,i+1,j,width = 24)
    root.mainloop()

I have tried removing all other bindings, to no avail. Interestingly, but a separate issue, If I use StringVar. set instead of self.insert, (see commented out line) the validate command runs once, and never again despite several focus changes. Using Python 3.8

Jim Robinson
  • 189
  • 1
  • 10

1 Answers1

1

The validation isn't designed to restore anything if the validation happens on focusout. The validation can only prevent characters from being added at the time they are added. You will have to add code to restore the previous value.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Well, If that is the case, I regret spending the time to get it to work. Thank You for the clarification. I will use the binding to restore the entry contents – Jim Robinson Mar 19 '20 at 19:58
  • @JimRobinson: you can certainly use `` if you wish. You can continue to use the validation, and use the `invalidcommand` option to specify a command to run which will restore the value. Either way lets you accomplish the same thing. – Bryan Oakley Mar 19 '20 at 20:00