Skip to content

Commit dc0a87d

Browse files
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 adbed2d) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 2e315d8 commit dc0a87d

File tree

6 files changed

+58
-3
lines changed

6 files changed

+58
-3
lines changed

Lib/tkinter/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2592,7 +2592,7 @@ def __init__(self, master, widgetName, cnf={}, kw={}, extra=()):
25922592
if kw:
25932593
cnf = _cnfmerge((cnf, kw))
25942594
self.widgetName = widgetName
2595-
BaseWidget._setup(self, master, cnf)
2595+
self._setup(master, cnf)
25962596
if self._tclCommands is None:
25972597
self._tclCommands = []
25982598
classes = [(k, v) for k, v in cnf.items() if isinstance(k, type)]
@@ -3011,6 +3011,8 @@ def type(self, tagOrId):
30113011
return self.tk.call(self._w, 'type', tagOrId) or None
30123012

30133013

3014+
_checkbutton_count = 0
3015+
30143016
class Checkbutton(Widget):
30153017
"""Checkbutton widget which is either in on- or off-state."""
30163018

@@ -3026,6 +3028,14 @@ def __init__(self, master=None, cnf={}, **kw):
30263028
underline, variable, width, wraplength."""
30273029
Widget.__init__(self, master, 'checkbutton', cnf, kw)
30283030

3031+
def _setup(self, master, cnf):
3032+
if not cnf.get('name'):
3033+
global _checkbutton_count
3034+
name = self.__class__.__name__.lower()
3035+
_checkbutton_count += 1
3036+
cnf['name'] = f'!{name}{_checkbutton_count}'
3037+
super()._setup(master, cnf)
3038+
30293039
def deselect(self):
30303040
"""Put the button in off-state."""
30313041
self.tk.call(self._w, 'deselect')

Lib/tkinter/dialog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class Dialog(Widget):
1111
def __init__(self, master=None, cnf={}, **kw):
1212
cnf = _cnfmerge((cnf, kw))
1313
self.widgetName = '__dialog__'
14-
Widget._setup(self, master, cnf)
14+
self._setup(master, cnf)
1515
self.num = self.tk.getint(
1616
self.tk.call(
1717
'tk_dialog', self._w,

Lib/tkinter/test/test_tkinter/test_widgets.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,32 @@ def test_configure_onvalue(self):
212212
widget = self.create()
213213
self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string')
214214

215+
def test_unique_variables(self):
216+
frames = []
217+
buttons = []
218+
for i in range(2):
219+
f = tkinter.Frame(self.root)
220+
f.pack()
221+
frames.append(f)
222+
for j in 'AB':
223+
b = tkinter.Checkbutton(f, text=j)
224+
b.pack()
225+
buttons.append(b)
226+
variables = [str(b['variable']) for b in buttons]
227+
self.assertEqual(len(set(variables)), 4, variables)
228+
229+
def test_same_name(self):
230+
f1 = tkinter.Frame(self.root)
231+
f2 = tkinter.Frame(self.root)
232+
b1 = tkinter.Checkbutton(f1, name='test', text='Test1')
233+
b2 = tkinter.Checkbutton(f2, name='test', text='Test2')
234+
235+
v = tkinter.IntVar(self.root, name='test')
236+
b1.select()
237+
self.assertEqual(v.get(), 1)
238+
b2.deselect()
239+
self.assertEqual(v.get(), 0)
240+
215241

216242
@add_standard_options(StandardOptionsTests)
217243
class RadiobuttonTest(AbstractLabelTest, unittest.TestCase):

Lib/tkinter/test/test_ttk/test_widgets.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,21 @@ def cb_test():
275275
self.assertEqual(cbtn['offvalue'],
276276
cbtn.tk.globalgetvar(cbtn['variable']))
277277

278+
def test_unique_variables(self):
279+
frames = []
280+
buttons = []
281+
for i in range(2):
282+
f = ttk.Frame(self.root)
283+
f.pack()
284+
frames.append(f)
285+
for j in 'AB':
286+
b = ttk.Checkbutton(f, text=j)
287+
b.pack()
288+
buttons.append(b)
289+
variables = [str(b['variable']) for b in buttons]
290+
print(variables)
291+
self.assertEqual(len(set(variables)), 4, variables)
292+
278293

279294
@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
280295
class EntryTest(AbstractWidgetTest, unittest.TestCase):

Lib/tkinter/tix.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ def __init__ (self, master=None, widgetName=None,
310310
del cnf[k]
311311

312312
self.widgetName = widgetName
313-
Widget._setup(self, master, cnf)
313+
self._setup(master, cnf)
314314

315315
# If widgetName is None, this is a dummy creation call where the
316316
# corresponding Tk widget has already been created by Tix
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix generation of the default name of :class:`tkinter.Checkbutton`.
2+
Previously, checkbuttons in different parent widgets could have the same
3+
short name and share the same state if arguments "name" and "variable" are
4+
not specified. Now they are globally unique.

0 commit comments

Comments
 (0)