gh-73588: Fix generation of the default name of tkinter.Checkbutton. (GH-97547)

Previously, checkbuttons in different parent widgets could have the same
short name and share the same state if arguments "name" and "variable" are
not specified. Now they are globally unique.
(cherry picked from commit adbed2d542)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
Miss Islington (bot) 2022-09-27 04:39:31 -07:00 committed by GitHub
parent 2e315d87ff
commit dc0a87d9a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 58 additions and 3 deletions

View file

@ -2592,7 +2592,7 @@ class BaseWidget(Misc):
if kw: if kw:
cnf = _cnfmerge((cnf, kw)) cnf = _cnfmerge((cnf, kw))
self.widgetName = widgetName self.widgetName = widgetName
BaseWidget._setup(self, master, cnf) self._setup(master, cnf)
if self._tclCommands is None: if self._tclCommands is None:
self._tclCommands = [] self._tclCommands = []
classes = [(k, v) for k, v in cnf.items() if isinstance(k, type)] classes = [(k, v) for k, v in cnf.items() if isinstance(k, type)]
@ -3011,6 +3011,8 @@ class Canvas(Widget, XView, YView):
return self.tk.call(self._w, 'type', tagOrId) or None return self.tk.call(self._w, 'type', tagOrId) or None
_checkbutton_count = 0
class Checkbutton(Widget): class Checkbutton(Widget):
"""Checkbutton widget which is either in on- or off-state.""" """Checkbutton widget which is either in on- or off-state."""
@ -3026,6 +3028,14 @@ class Checkbutton(Widget):
underline, variable, width, wraplength.""" underline, variable, width, wraplength."""
Widget.__init__(self, master, 'checkbutton', cnf, kw) Widget.__init__(self, master, 'checkbutton', cnf, kw)
def _setup(self, master, cnf):
if not cnf.get('name'):
global _checkbutton_count
name = self.__class__.__name__.lower()
_checkbutton_count += 1
cnf['name'] = f'!{name}{_checkbutton_count}'
super()._setup(master, cnf)
def deselect(self): def deselect(self):
"""Put the button in off-state.""" """Put the button in off-state."""
self.tk.call(self._w, 'deselect') self.tk.call(self._w, 'deselect')

View file

@ -11,7 +11,7 @@ class Dialog(Widget):
def __init__(self, master=None, cnf={}, **kw): def __init__(self, master=None, cnf={}, **kw):
cnf = _cnfmerge((cnf, kw)) cnf = _cnfmerge((cnf, kw))
self.widgetName = '__dialog__' self.widgetName = '__dialog__'
Widget._setup(self, master, cnf) self._setup(master, cnf)
self.num = self.tk.getint( self.num = self.tk.getint(
self.tk.call( self.tk.call(
'tk_dialog', self._w, 'tk_dialog', self._w,

View file

@ -212,6 +212,32 @@ class CheckbuttonTest(AbstractLabelTest, unittest.TestCase):
widget = self.create() widget = self.create()
self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string') self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string')
def test_unique_variables(self):
frames = []
buttons = []
for i in range(2):
f = tkinter.Frame(self.root)
f.pack()
frames.append(f)
for j in 'AB':
b = tkinter.Checkbutton(f, text=j)
b.pack()
buttons.append(b)
variables = [str(b['variable']) for b in buttons]
self.assertEqual(len(set(variables)), 4, variables)
def test_same_name(self):
f1 = tkinter.Frame(self.root)
f2 = tkinter.Frame(self.root)
b1 = tkinter.Checkbutton(f1, name='test', text='Test1')
b2 = tkinter.Checkbutton(f2, name='test', text='Test2')
v = tkinter.IntVar(self.root, name='test')
b1.select()
self.assertEqual(v.get(), 1)
b2.deselect()
self.assertEqual(v.get(), 0)
@add_standard_options(StandardOptionsTests) @add_standard_options(StandardOptionsTests)
class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): class RadiobuttonTest(AbstractLabelTest, unittest.TestCase):

View file

@ -275,6 +275,21 @@ class CheckbuttonTest(AbstractLabelTest, unittest.TestCase):
self.assertEqual(cbtn['offvalue'], self.assertEqual(cbtn['offvalue'],
cbtn.tk.globalgetvar(cbtn['variable'])) cbtn.tk.globalgetvar(cbtn['variable']))
def test_unique_variables(self):
frames = []
buttons = []
for i in range(2):
f = ttk.Frame(self.root)
f.pack()
frames.append(f)
for j in 'AB':
b = ttk.Checkbutton(f, text=j)
b.pack()
buttons.append(b)
variables = [str(b['variable']) for b in buttons]
print(variables)
self.assertEqual(len(set(variables)), 4, variables)
@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
class EntryTest(AbstractWidgetTest, unittest.TestCase): class EntryTest(AbstractWidgetTest, unittest.TestCase):

View file

@ -310,7 +310,7 @@ class TixWidget(tkinter.Widget):
del cnf[k] del cnf[k]
self.widgetName = widgetName self.widgetName = widgetName
Widget._setup(self, master, cnf) self._setup(master, cnf)
# If widgetName is None, this is a dummy creation call where the # If widgetName is None, this is a dummy creation call where the
# corresponding Tk widget has already been created by Tix # corresponding Tk widget has already been created by Tix

View file

@ -0,0 +1,4 @@
Fix generation of the default name of :class:`tkinter.Checkbutton`.
Previously, checkbuttons in different parent widgets could have the same
short name and share the same state if arguments "name" and "variable" are
not specified. Now they are globally unique.