4

I'm trying to bind a function to my Tkinter root that reacts when the user presses return. Here's the code:

def returnPressed(event):
    print repr(event.widget)

master = Tk()

master.bind("<Return>", returnPressed)
myStringVar = StringVar(value="Foo")
myEntry = Entry(master, textvariable=myStringVar)
myEntry.grid()

master.mainloop()

This works, however now I need to get the StringVar variable of the Entry while in the returnPressed function.

I have the widget, but I can't get the variable object. I know how to get the variable content but I really need the object.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Tekar
  • 105
  • 2
  • 8
  • perhaps try doing `master.bind("",lambda: returnPressed(myStringVar))`? – IT Ninja Jan 31 '13 at 15:41
  • 3
    why do you need the object? In my experience, StringVars are almost never necessary unless you intend to put a trace on the variable, which you're not doing. Maybe you're working too hard to accomplish your task. – Bryan Oakley Jan 31 '13 at 17:59
  • I agree with Bryan. Check out [his other answer](http://stackoverflow.com/a/10027212/1217270) ;-] – Honest Abe Feb 02 '13 at 06:47
  • You might also benefit from studying the first example of [this answer](http://stackoverflow.com/a/10065345/1217270). It shows how to retrieve an Entry's contents and store that as the value of a class attribute. – Honest Abe Feb 02 '13 at 07:18

4 Answers4

4

To get any attribute of a widget, use cget:

print(event.widget.cget("textvariable"))

However, in this particular instance what you get back is the internal name of the stringvar rather than the stringvar itself, due to some implementation details. So, you'll have to find another way.

The easiest thing might be to pass the stringvar instance to the function that is called. You can do this with lambda or functools.partial. For example:

def returnPressed(event, v):
    ...
master.bind("<Return>",lambda event, v=myStringVar: returnPressed(event, v))
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
1

I work around tkinter's bad design in this regard by using an outer object that contains a ref to both the Entry and the StringVar textvariable it uses, for easy access (a tuple could also work):

class EntryWithText()
    def __init__(self):
        self.entry = None
        self.textvariable = StringVar() #or None


entryWithTexts = []
for i in range(10):
    entryWithTexts[i] = EntryWithText()
    entryWithTexts[i].entry = Entry(master, textvariable=entryWithTexts[i].textvariable, ...)
    entryWithTexts[i].textvariable.set("this is entry number " + str(i))

Undoubtedly the extra level of indirection will have some impact if you have 100k such entries, but I'm writing small utilities here, so it's no concern.

Engineer
  • 8,529
  • 7
  • 65
  • 105
0

Thought I'd pass on that I've discovered a way.

Reading the widget's 'textvariable' option value gives you the name of the Variable previously set. Initializing a StringVar instance with this name will give you a new instance that appears to be linked to the original. Initializing without a value preserves the current value. I say 'linked' since the new instance and the original will not be the same object(as verified by their ids) but setting the value on the new instance will change the value in object in the widget. See the docs for Variable._init_.

    name = event.widget['textvariable']
    var = StringVar(root, name=name)
-1

I know this is an old question, but I think I found a workaround for this limitation of tkinter by internalizing the control variable. Then you don't need to declare a separate value when creating your widgets:

class DynamicLabel(tkinter.Label):
    def __init__(self, master, *args, **kwargs):
        super().__init__(master=master, *args, **kwargs)
        self.master = master
        self.value=None
        for kwarg, value in kwargs.items():
            if kwarg == 'textvariable':
                if not isinstance(value, tkinter.StringVar):
                    raise ValueError('Textvariable must be StringVar')
                self.value = value
        if not self.value:
            self.value = tkinter.StringVar()
            self.config(textvariable=self.value)

    def set(self, txt):
        self.value.set(txt)

    def get(self):
        return self.value.get()

What this does is creating a proxy for the control variable with self.value.

Then we will iterate through all kwargs and if textvariable is found we make self.value a reference to it.

If no textvariable was set when creating the widget, we just create our own and assign it to the widget with self.config(textvariable=self.value)

All that is left then is to create set and get methods to access the control variable.

I guess the above could be even further simplified by ignoring whichever textvariable was assigned when creating the widget and always creating a new one from scratch.

You could even make self.value a property to be able to interact with self.value directly

btonasse
  • 90
  • 1
  • 7