Created on 2012-06-22 09:27 by mark, last changed 2012-11-08 22:10 by zach.ware.
|msg163387 - (view)||Author: Mark Summerfield (mark)||Date: 2012-06-22 09:27|
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.)
|msg163553 - (view)||Author: Terry J. Reedy (terry.reedy) *||Date: 2012-06-23 08:51|
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.
|msg163644 - (view)||Author: Mark Summerfield (mark)||Date: 2012-06-23 16:55|
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 ### 0 <class 'int'> 1 <class 'int'> 0 <class 'int'>
|msg163686 - (view)||Author: Terry J. Reedy (terry.reedy) *||Date: 2012-06-23 22:06|
Just that, which I used to verify with 2.7.3 and 3.3.0a4 in Win7 and do some more experiments: bv.set/get happily accept *and return* 2, -1, bv.set(2.2) 'works' bv.get() then raises TypeError: must be str, not float This will annoy you ;-) bv.set('1'); bv.get() returns True, '0'=>False most other string inputs raise ValueError: invalid literal for getboolean() (but see below). 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. 1) add BooleanVar.set to convert Python bool to tcl bool. Other python objects could be converted, except that some people might be using python strings that become tcl bools (as above) which come back as python bools. 2. change getboolean to not convert python bool (if that is where is happens) and do convert int to bool (just 0,1 or all ints?). 3. change BooleanVar.get to convert 0/1 (or all returns?) to False/True.
|msg163747 - (view)||Author: Mark Summerfield (mark)||Date: 2012-06-24 07:36|
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.
|msg163748 - (view)||Author: Mark Summerfield (mark)||Date: 2012-06-24 07:41|
Oh, and I forgot to say that I think StringVar.set(x) should do str(x) and StringVar.get() should return a str.
|msg163770 - (view)||Author: klappnase (klappnase)||Date: 2012-06-24 11:19|
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. Now when it comes to change Tkinter's behavior I'd tend to be careful. For example BooleanVar.get() could easily be changed to always return True/False. But imho this change would only make sense if you add strict input value checking for set() either, otherwise we just change one incoherency with another. But this would mean that we might end up breaking a lot of working code, for what seems to me a primarily aesthetic reason. 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. But again, this looks like quite a lot of work, will break running code and add no significant benefit besides a cosmetic improvement. So my vote goes to accepting that 0 and 1 are ok boolean values in Tkinter and changing no more than misleading docstrings, as in this example of BooleanVar.get(): def get(self): """Return the value of the variable as 1 (True) or 0 (False).""" return self._tk.getboolean(self._tk.globalgetvar(self._name))
|msg163824 - (view)||Author: Terry J. Reedy (terry.reedy) *||Date: 2012-06-24 19:25|
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.
|msg163905 - (view)||Author: Mark Summerfield (mark)||Date: 2012-06-25 06:36|
How about a compromise? Deprecate (but keep BooleanVar) and add BoolVar with proper True/False behavior to match the other *Vars?
|msg163923 - (view)||Author: klappnase (klappnase)||Date: 2012-06-25 10:16|
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. If set() got 0/1 (as integers) or True/False before, getboolean() will return 0/1 and this appears to me to be the only bug here. But this is not at all a sole BooleanVar bug, e.g.: >>> 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: "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." But when the input type is not proper you can do e.g.: >>> 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.
|msg163924 - (view)||Author: klappnase (klappnase)||Date: 2012-06-25 10:19|
"...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.
|msg166494 - (view)||Author: Christian Rickert (crickert)||Date: 2012-07-26 16:53|
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) [GCC 4.6.3] on linux2
|msg175197 - (view)||Author: Zachary Ware (zach.ware) *||Date: 2012-11-08 22:10|
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.
messages: + msg175197
versions: + Python 2.7, Python 3.3, Python 3.4
messages: + msg166494
versions: - Python 2.7, Python 3.3
|2012-06-25 10:19:12||klappnase||set||messages: + msg163924|
|2012-06-25 10:16:50||klappnase||set||messages: + msg163923|
|2012-06-25 06:36:05||mark||set||messages: + msg163905|
|2012-06-24 19:25:35||terry.reedy||set||messages: + msg163824|
messages: + msg163770
|2012-06-24 07:41:26||mark||set||messages: + msg163748|
|2012-06-24 07:36:42||mark||set||messages: + msg163747|
|2012-06-23 22:06:45||terry.reedy||set||messages: + msg163686|
|2012-06-23 16:55:19||mark||set||messages: + msg163644|
+ gpolo, terry.reedy, roger.serwy|
title: tkinter.BooleanVar.get() docstring is wrong -> tkinter.BooleanVar.get() behavior and docstring disagree
messages: + msg163553
versions: + Python 2.7, Python 3.3