Skip to content

Commit adbed2d

Browse files
pythongh-73588: Fix generation of the default name of tkinter.Checkbutton. (pythonGH-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.
1 parent 68c46ae commit adbed2d

File tree

6 files changed

+58
-3
lines changed

6 files changed

+58
-3
lines changed

Lib/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/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/__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/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)