Skip to content

Commit 04aa15f

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 dfe23ee commit 04aa15f

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
@@ -2619,7 +2619,7 @@ def __init__(self, master, widgetName, cnf={}, kw={}, extra=()):
26192619
if kw:
26202620
cnf = _cnfmerge((cnf, kw))
26212621
self.widgetName = widgetName
2622-
BaseWidget._setup(self, master, cnf)
2622+
self._setup(master, cnf)
26232623
if self._tclCommands is None:
26242624
self._tclCommands = []
26252625
classes = [(k, v) for k, v in cnf.items() if isinstance(k, type)]
@@ -3038,6 +3038,8 @@ def type(self, tagOrId):
30383038
return self.tk.call(self._w, 'type', tagOrId) or None
30393039

30403040

3041+
_checkbutton_count = 0
3042+
30413043
class Checkbutton(Widget):
30423044
"""Checkbutton widget which is either in on- or off-state."""
30433045

@@ -3053,6 +3055,14 @@ def __init__(self, master=None, cnf={}, **kw):
30533055
underline, variable, width, wraplength."""
30543056
Widget.__init__(self, master, 'checkbutton', cnf, kw)
30553057

3058+
def _setup(self, master, cnf):
3059+
if not cnf.get('name'):
3060+
global _checkbutton_count
3061+
name = self.__class__.__name__.lower()
3062+
_checkbutton_count += 1
3063+
cnf['name'] = f'!{name}{_checkbutton_count}'
3064+
super()._setup(master, cnf)
3065+
30563066
def deselect(self):
30573067
"""Put the button in off-state."""
30583068
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)