-1

I'm using Tkinter's "validatecommand" function to validate inputs from a entry box. I want to pass my class object so that the validation-function can request information from the object. However, it seems that the validatecommand function turns everything I pass into strings. Because of this the validation-function now has __main__.foo object at 0x042981B0 but as string. How can I instead pass the original __main__.foo? It currently looks like this (pseudo-code):

class foo(object):
    def start(program):
        self.stuff = 5 #stuff changes while the program is running
        tkinter_stuff(program)
def tkinter_stuff(program):
    Entry = tkinter.Entry(validatecommand = (window.register(validate_entry), '%P', program))
def validate_entry(entry, program): #checks if current stuff + the amount of staff that would be added over this entry box is <= 20
    if int(entry) + program.get_stuff() <= 20:
        return True
    return False
program = foo() #there are other classes that create their own program and overwrite the one the entry uses, so I can't rely on this one
program.start(program)

actual code:

import tkinter
class foo(object):
    def __init__(self):
        self.stuff = 5 #stuff changes while the program is running
    def start(self, program):
        tkinter_stuff(program)
    def get_stuff(self):
        return self.stuff
def tkinter_stuff(program):
    window = tkinter.Tk(className = 'window')
    window.geometry('50x50')
    print(program, type(program))
    Entry = tkinter.Entry(window, width = 10, validate = 'key', validatecommand = (window.register(validate_entry), '%P', program))
    Entry.place(x = 10, y = 10)
    window.update()
def validate_entry(entry, program): #checks if current stuff + the amount of staff that would be added over this entry box is <= 20
    print(program, type(program))
    if int(entry) + program.get_stuff() <= 20:
        return True
    return False
program = foo() #there are other classes that create their own program and overwrite the one the entry uses, so I can't rely on this one
program.start(program)
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
aszubat
  • 11
  • 3
  • Look at [this](https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/entry-validation.html). From what I understand you shouldn't pass in `program` in the `validatecommand`. Why do you need to pass in the `program` variable? Can't you use the global value that you already have? – TheLizzard Jun 27 '21 at 14:27
  • Please provide actual code which illustrates the problem. Your pseudocode won't work due to several reasons. – Bryan Oakley Jun 27 '21 at 14:33
  • @TheLizzard in my actual program the 'program' variable isn't global. I want to use the entry box to add stuff, but I don't want there to be more than 20 stuff. That's why I pass the 'program' variable, so the 'validatecommand' can check how much stuff already exists. – aszubat Jun 27 '21 at 14:37
  • Please try to actually run the code you posted. Even with your edit it won't run. It would also help if you followed PEP8 naming conventions. – Bryan Oakley Jun 27 '21 at 14:45
  • @BryanOakley this should be it, hope that helps – aszubat Jun 27 '21 at 14:58
  • It helps, but your code never calls `mainloop` so it exits immediately. – Bryan Oakley Jun 27 '21 at 15:02
  • 1
    Why is your `validate_entry` function outside of the class? If it were inside the class it could just use `self.stuff` rather than you needing to pass the instance to the function. – Bryan Oakley Jun 27 '21 at 15:03
  • @BryanOakley The whole project is rather big so I decided to put the different things in different files and then to import them. So all the tkinter stuff has it's own file, that's why it isn't part of the class. – aszubat Jun 27 '21 at 15:08
  • 1
    This code would be much simpler if the validate function was part of the class. – Bryan Oakley Jun 27 '21 at 15:15

1 Answers1

1

Try this:

import tkinter as tk


class Entry(tk.Entry):
    def __init__(self, master=None, args=tuple(), validatecommand=None, **kwargs):
        if validatecommand is not None:
            self.args = args
            self.callers_function = validatecommand[0]
            validatecommand = (root.register(self.validatecommand), *validatecommand[1:])
        super().__init__(master, validatecommand=validatecommand, **kwargs)

    def validatecommand(self, *args):
        return self.callers_function(*args, *self.args)


class Foo:
    def __init__(self):
        pass


def validate_entry(entry, program):
    print(type(entry), type(program))
    return True


program = Foo()

root = tk.Tk()

# Make sure it's not `root.register(validate_entry)`:
entry = Entry(root, validate="key", validatecommand=(validate_entry, "%P"),
              args=(program, ))
entry.pack()

root.mainloop()

I just made a wrapper class that will call the validatecommand with the args that were specified when creating the Entry.

TheLizzard
  • 7,248
  • 2
  • 11
  • 31
  • Took a little bit adjusting but is now working as I wanted. Thank you VERY much, I wouldn't have come up with that on my own. – aszubat Jun 27 '21 at 15:56
  • Should the question title be updated to ask the question this answer replies to, rather than asking for something impossible? – Charles Duffy Jun 27 '21 at 19:12
  • @CharlesDuffy I agree that the answer is actually a work around for this case but saying that it's impossible, ... The string that OP gets contains the object's id and they can use [this](https://stackoverflow.com/questions/15011674/is-it-possible-to-dereference-variable-ids) to get the object back. But my work around is much safer. – TheLizzard Jun 27 '21 at 19:15
  • @TheLizzard, ... What I'd like to see addressed is that while this answer is what the OP needs, I doubt that the title is going to be useful for helping other people with the same problem find it (while preventing people it doesn't actually help from clicking through). Insofar as you understand the OP's problem well enough the give them what they need as opposed to what they asked for, you're probably in a better place than I am to come up with a title that more accurately reflects the problem actually being solved. – Charles Duffy Jun 27 '21 at 19:25
  • @CharlesDuffy I am terrible at coming up with titles :D. I would suggest a title like: "*passing arguments in tkinter's Entry validatecommand*" – TheLizzard Jun 27 '21 at 19:28