nick topper
nick topper

Reputation: 105

Why is Tkinter.StringVar().get() trying to cast it's own value as an integer?

So I have a slider who updates an entry when changed, and that entry updates the slider when changed. Basically they both represent the same value but with two different methods of input. The relevant methods look like this (abridged):

class MyWindow(Frame):

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack(fill=BOTH, expand=1)
        self.makeWidgets()

    def makeWidgets(self):

        #controls to set the radius of a circle
        #slider
        self.radSlider = Scale(self, command=self.updateEntry)
        self.radSlider.pack()

        #entry
        self.radVar = StringVar()
        vcmd = (self.master.register(self.validateInt),
            '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
        self.radEntry = Entry(self, 
                              validate = 'key', 
                              validatecommand = vcmd, 
                              textvariable=self.radVar)
        self.radEntry.pack()
        self.radVar.trace("w", self.updateSlide)

    def updateEntry(self, *event):
        rad = event[0]#first element of event is value of slider, as string
        self.radVar.set(rad)

    def updateSlide(self, *event):
        if not self.radVar.get():#if entry is an empty string, set slider to zero
            value = 0
        else:
            value = int(self.radVar.get())
        self.radSlider.set(value)        

    def validateInt(self, action, index, value_if_allowed, prior_value, text, *args):
    """
    validates an entry to integers and empty string only
    """
        if (value_if_allowed == ""):
            return True
        if text in '0123456789':
            try:
                int(value_if_allowed)
                return True
            except:
                return False
        else:
            return False

It works fine, but when radEntry is an empty string:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1410, in __call__
    return self.func(*args)
  File "C:\Users\ntopper\Desktop\Collector\NewObjDialog.py", line 127, in update Slide
    if not self.radVar.get():
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 283, in get
    return getint(self._tk.globalgetvar(self._name))
ValueError: invalid literal for int() with base 10: ''

One workaround is to never allow the entry to be an empty string, but having a zero that can't be deleted in the entry box is mildly infuriating. (to set the box to 241, you would first set the box to 410, then 41, then 241)

The other option is just to ignore the error message because it does not crash my program, but that's sort of sloppy.

It seems that Tkniter.StringVar().get() is trying to cast it's own string value as an integer, why is this?

Upvotes: 3

Views: 547

Answers (1)

Terry Jan Reedy
Terry Jan Reedy

Reputation: 19144

Here is the simpler is_int (or blank) Entry validator that I just wrote for an Idle dialog.

# at module level
def is_int(s):
    "Return 's is blank or represents an int'"
    if not s:
        return True
    try:
        int(s)
        return True
    except ValueError:
        return False
...
# inside widget_subclass.__init__
       self.is_int = self.register(is_int)
...
# inside widget_subclass method
           Entry(entry_area, textvariable=var, validate='key',
                 validatecommand=(self.is_int, '%P')  
                ).grid(row=row, column=1, sticky=NSEW, padx=7)

As for your error, when I fix the validate docstring indent like so

    def validateInt(self, action, index, value_if_allowed, prior_value, text, *args):
        """validates an entry to integers and empty string only"""
        if value_if_allowed == "":

and run with 2.7.8 on Win7, your code works fine with no error message. Changing either slider or text changes the other, as you designed.

Looking at my copy of Python27\lib\lib-tk\Tkinter.py,

    return getint(self._tk.globalgetvar(self._name))

is on line 321 inside the IntVar.get method, where I expected it. Line 283, as in the traceback above, is in the StringVar.__init__ docstring. So my best guess is that you are running a earlier version of 2.x and that your copy of Tkinter.py has a severe bug.

Upvotes: 1

Related Questions