New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tkinter.BooleanVar.get() behavior and docstring disagree #59338
Comments
Python 3.2.2 (default, Jun 4 2012, 11:15:16)
[GCC 4.4.5] on linux2
Type "copyright", "credits" or "license()" for more information.
>>> from tkinter import *
>>> help(BooleanVar.get)
Help on function get in module tkinter: get(self)
Return the value of the variable as a bool. On my system it actually returns an int. (I wish it did return a bool though.) |
The bug is the mismatch between doc and behavior. Unless someone can explain why the seemingly reasonable docstring is wrong, I would consider changing the behavior a possible fix. Can you add minimal test code that gives you an int? I should check windows and someone should check 2.7, doc and behavior. |
Did you mean formal test code? Or just an example like this: from tkinter import *
tk = Tk()
bv = BooleanVar()
print(bv.get(), type(bv.get()))
bv.set(True)
print(bv.get(), type(bv.get()))
bv.set(False)
print(bv.get(), type(bv.get())) ### output ### |
Just that, which I used to verify with 2.7.3 and 3.3.0a4 in Win7 and do some more experiments: This will annoy you ;-) bv.set('1'); bv.get() returns True, '0'=>False Doc: "There are many useful subclasses of Variable already defined: StringVar, IntVar, DoubleVar, and BooleanVar." Looking into tkinter.__init__, *all* subclasses just inherit Variable.set(self, value): return self._tk.globalsetvar(self._name, value). So there is no input validation or conversion. I wonder is here should be? It appears that anything can be set to anything. I was thinking that everything was converted to a string, and the error message for 2.2 above suggests that, but the (surprising to me) different behavior of 1 and '1' says that is not right. So does iv=IntVar(); iv.set(2.2); iv.get() returning 2. Variable.get(self) returns self._tk.globalgetvar(self._name). String/Int/Double/Var call str/int/float as appropriate. BooleanVar calls tk.getboolean which is supposed to "Convert true and false to integer values 1 and 0.". Well, it should not do that and does not do that for tcl false and true, as with '0' and '1'. Further checking shows that 'no', 'false', 'yes', 'true' or any prefix thereof return False or True as desired. So getboolean was partially changed after the introduction of bool to return bool instead int for tcl booleans, but it was not changed to convert ints to bool. And if one inputs a Python bool, it somehow gets converted to an int instead of being returned as is. I see three possible fixes, but I do not know enough of tk/inter usage to choose.
|
I think that BooleanVar.set(x) should do bool(x) on its argument (and raise an exception if this isn't valid) and BooleanVar.get() should return a bool. Similarly I think that IntVar.set(x) should do int(x) and IntVar.get() should return an int, and that DoubleVar.set(x) should do float(x) and should return a float. I will mention this issue on tkinter-discuss and encourage people to comment. |
Oh, and I forgot to say that I think StringVar.set(x) should do str(x) and StringVar.get() should return a str. |
The behavior of Tkinter when handling boolean values appears to be a bit erratic at times, not only BooleanVar.get() returns 0/1 instead of True/False (e.g. see the thread at tkinter-discuss: http://mail.python.org/pipermail/tkinter-discuss/2012-June/003176.html ). This is apparently because tk itself uses the strings '0' and '1' as boolean values, so behind the scenes Tkinter has to convert any bool into a string when setting a value and the string back into a bool when retrieving it, and it seems like most of the Tkinter code (and docstrings) was written in the good ol' days when Python used 0 / 1 itself. And of course, I don't see much use in isolated changes of the behavior of one function here and another method there. I feel that if the behavior of Tkinter concerning bool values was to be changed, there should at least be a thorough change of *any* Tkinter method that gets / sets boolean values so we at least end up with an overall coherent solution. def get(self):
"""Return the value of the variable as 1 (True) or 0 (False)."""
return self._tk.getboolean(self._tk.globalgetvar(self._name)) |
Mark: "Variable.get(self) returns self._tk.globalgetvar(self._name). String/Int/Double/Var call str/int/float as appropriate." was meant to say that String/Int/Double/Var.get(self) already call str/int/float on the result of self._tk.globalgetvar(self._name) before returning it. BooleanVar.get does not call bool, but instead calls self._tk.getboolean, which fails to always return a boolean. Klappnase: I disagree, somehow. bool is a subclass of int, so that False, True are arithmetically indistinguishable from 0, 1. The main difference is on display and 'type(x) = int/bool' comparisons (which should be isinstance()). BooleanVar.get already returns False, True for the tcl boolean values '0', '1' set by tk rather than the user (which I expect should be the usual case for retrieval). So a Python/tkinter program has to be prepared to get proper booleans anyway. Since the purpose of Variables is to synchronize values between user code and tk, TypeVar().set(x).get() should be x when has the proper type. That is now true for everything but bool/Boolean. I do wonder whether not converting or rejecting bad inputs to .set could cause problems with tk, but maybe some of that is handled later (and silently? if so bad) within _tkinter. I could be persuaded that a behavior fix should only be applied to 3.3. |
How about a compromise? Deprecate (but keep BooleanVar) and add BoolVar with proper True/False behavior to match the other *Vars? |
I agree that get() should better return a boolean in any case, however the bug then is not one of BooleanVar but rather one of tk.getboolean(). I did some experimenting with get() and it seems that there are three possibilities: getboolean() returns True/False as it should when the variable value was previously set() to anything Tcl accepts as boolean value (like 'yes'/'no', '0'/'1', 'on'/'off', 'true'/'false'). If something which is not a proper Tcl-bool was passed to set() before, getboolean will produce a TclError or TypeError. >>> root=Tk()
>>> root.tk_strictMotif()
0
>>> root.tk_strictMotif(1)
1 You will find this everywhere in Tkinter where getboolean() is used, so I think that rather this should be fixed in _tkinter.c than applying a workaround to BooleanVar.get() and leaving all other uses of getboolean() as they are; even worse, if BooleanVar.set() was changed to accept only True/False and getboolean() left as it is, you could not pass the return value of getboolean() any longer to BooleanVar.get() which would be really bad. As far as set() is concerned, it should at least allow everything that Tcl accepts as boolean, according to http://wiki.tcl.tk/16235 these are at least the above mentioned 'yes'/'no', '0'/'1', 'on'/'off', 'true'/'false' (btw. all of these seem to be ok even case insensitive on the tcl side) plus of course 0/1 and True/False. Otherwise it might break existing code. Terry: >>> d = DoubleVar()
>>> d.set('1.1')
>>> d.get()
1.1
>>> i = IntVar()
>>> i.set(True)
>>> i.get()
1
>>> I think the Variable classes can also be considered convenience objects that save the application programmer a lot of headaches when they have to convert Python objects into Tcl which usually expects strings, so I think there is nothing wrong with set() accepting "improper" values. |
"...you could not pass the return value of getboolean() any longer to BooleanVar.get() which would be really bad." Ooops, typo, should be BooleanVar.set() of course. |
Noticed this odd behaviour with BooleanVar.get() as well: class MyCheckbutton(Checkbutton):
def __init__(self, parent, **options):
Checkbutton.__init__(self, parent, **options)
self.var = BooleanVar()
self.configure(indicatoron=False, command=self.cb, variable=self.var)
print(self.var.get) # "<bound method BooleanVar.get of <tkinter.BooleanVar object at 0x245c310>>"
print(self.var.get()) # "0"
def cb(self, *events): # button callback (manual toggle)
print(self.var.get) # <bound method BooleanVar.get of <tkinter.BooleanVar object at 0x245c310>>
print(self.var.get()) # True Python 3.2.3 (default, May 3 2012, 15:51:42) |
I don't think 2.7 and 3.3 were meant to be removed from this one and we're now in 3.4 time. |
Serhiy, what do you think is the proper disposition of this issue *now*? |
I have no strong opinion. Definitely getboolean() (and other getXXX) is not consistent. The question is what should getboolean (and BooleanVar.get) always return, bool or int? Note that since 8.6 Tcl doesn't use special boolean type internally and use integers 0 and 1 to represent boolean values. So any boolean values returned from Tk will be converted by Tkinter to 0/1. |
Here is a patch.
Similar changes should be made for getint() and getdouble(). |
What are your thoughts about the patch Terry? |
New changeset dedf481ec2be by Serhiy Storchaka in branch '2.7': New changeset 117f45749359 by Serhiy Storchaka in branch '3.4': New changeset 38747f32fa7b by Serhiy Storchaka in branch 'default': |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: