0

Why do I get a DoubleVar has no attribute _report_exception error when trying to define a custom tkinter entry widget that accepts only floats?

It happens when I delete the contents of the entry widget to enter a new floating point number

I've tried to follow the example taken from the documentation here except for changing tk.StringVar() to tk.DoubleVar() they look to be identical..

example of using custom entry widget:

self.depth_label = tk.Label(self, text="Maximum Depth")
self.depth_label.grid(row=1, column = 1)
self.depth_entry = FloatEntry(self, default=255.0)
self.depth_entry.grid(row=1, column = 2)

custom entry class

class FloatEntry(tk.Entry):

    def __init__(self, master, default=0.0, **kwargs):

        tk.Entry.__init__(self, master, **kwargs)
        self.default = default
        self.value = self.default
        self.float = tk.DoubleVar()
        self.float.set(self.value)
        self.float.trace("w", self.__callback)
        self.config(textvariable=self.float)

    def __callback(self, *dummy):
        value = self.float.get()
        new_value = self.validate(value)
        if new_value is None:
            self.float.set(self.default)
        elif new_value != value:
            self.value = new_value
            self.float.set(self.new_value)
        else:
            self.value = value

    def validate(self, value):
        try:
            if value:
                value = float(value)
            return value
        except ValueError:
            return None
Pythonista
  • 11,377
  • 2
  • 31
  • 50

2 Answers2

0

The problem happens when you delete the last character in the entry. The entry value becomes an empty string, and because you've tied the Entry widget to a DoubleVar, tkinter will try to convert the value to a double and will fail since an empty string is not a valid floating point number.

There are at least a couple of solutions. One is to tie the Entry widget to a StringVar, You can then add code to handle the special case where the widget is empty.

Another solution is to use the built-in entry widget validation code that can prevent the widget from ever having a non-valid floating point value. There a lengthy answer on stackoverflow dealing with how to use the validation feature here: https://stackoverflow.com/a/4140988/7432.

Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
-1

You should use validatecommand to check the input from the entry and return values against that.

import tkinter

def validate(action, index, value_if_allowed,
                       prior_value, text, validation_type, trigger_type, widget_name):
    text = entry.get()
    if text in '0123456789':
        try:
            float(value_if_allowed)
            return True
        except ValueError:
            return False
    else:
        return False

window = tkinter.Tk()

entry = tkinter.Entry(window)
vcmd = (window.register(validate),'%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')

entry.configure(validate="key", validatecommand=vcmd)

entry.pack()

window.mainloop()
Inkblot
  • 708
  • 2
  • 8
  • 19
  • This will not work. If the user enters the string "3.14" it will fail the test `if text in '0123456789'`. The function will thus return false. Also, when you call `entry.get()`, that will return the value before the edit. You have the right instincts to use `validatecommand`, but your implementation is critically flawed. – Bryan Oakley Nov 29 '15 at 22:47