From adbed2d542a815b8175db965742211856b19b52f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 27 Sep 2022 14:05:05 +0300 Subject: 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. --- Lib/test/test_tkinter/test_widgets.py | 26 ++++++++++++++++++++++ Lib/test/test_ttk/test_widgets.py | 15 +++++++++++++ Lib/tkinter/__init__.py | 12 +++++++++- Lib/tkinter/dialog.py | 2 +- Lib/tkinter/tix.py | 2 +- .../2022-09-25-20-42-33.gh-issue-73588.uVtjEA.rst | 4 ++++ 6 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-09-25-20-42-33.gh-issue-73588.uVtjEA.rst diff --git a/Lib/test/test_tkinter/test_widgets.py b/Lib/test/test_tkinter/test_widgets.py index 1cddbe1..6cde1cc 100644 --- a/Lib/test/test_tkinter/test_widgets.py +++ b/Lib/test/test_tkinter/test_widgets.py @@ -212,6 +212,32 @@ class CheckbuttonTest(AbstractLabelTest, unittest.TestCase): widget = self.create() 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) class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): diff --git a/Lib/test/test_ttk/test_widgets.py b/Lib/test/test_ttk/test_widgets.py index eb0cc93..6f47ccb 100644 --- a/Lib/test/test_ttk/test_widgets.py +++ b/Lib/test/test_ttk/test_widgets.py @@ -275,6 +275,21 @@ class CheckbuttonTest(AbstractLabelTest, unittest.TestCase): self.assertEqual(cbtn['offvalue'], 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) class EntryTest(AbstractWidgetTest, unittest.TestCase): diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 2963202..3564469 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -2619,7 +2619,7 @@ class BaseWidget(Misc): if kw: cnf = _cnfmerge((cnf, kw)) self.widgetName = widgetName - BaseWidget._setup(self, master, cnf) + self._setup(master, cnf) if self._tclCommands is None: self._tclCommands = [] classes = [(k, v) for k, v in cnf.items() if isinstance(k, type)] @@ -3038,6 +3038,8 @@ class Canvas(Widget, XView, YView): return self.tk.call(self._w, 'type', tagOrId) or None +_checkbutton_count = 0 + class Checkbutton(Widget): """Checkbutton widget which is either in on- or off-state.""" @@ -3053,6 +3055,14 @@ class Checkbutton(Widget): underline, variable, width, wraplength.""" 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): """Put the button in off-state.""" self.tk.call(self._w, 'deselect') diff --git a/Lib/tkinter/dialog.py b/Lib/tkinter/dialog.py index 8ae2140..36ae6c2 100644 --- a/Lib/tkinter/dialog.py +++ b/Lib/tkinter/dialog.py @@ -11,7 +11,7 @@ class Dialog(Widget): def __init__(self, master=None, cnf={}, **kw): cnf = _cnfmerge((cnf, kw)) self.widgetName = '__dialog__' - Widget._setup(self, master, cnf) + self._setup(master, cnf) self.num = self.tk.getint( self.tk.call( 'tk_dialog', self._w, diff --git a/Lib/tkinter/tix.py b/Lib/tkinter/tix.py index 44ecae1..ce21826 100644 --- a/Lib/tkinter/tix.py +++ b/Lib/tkinter/tix.py @@ -310,7 +310,7 @@ class TixWidget(tkinter.Widget): del cnf[k] self.widgetName = widgetName - Widget._setup(self, master, cnf) + self._setup(master, cnf) # If widgetName is None, this is a dummy creation call where the # corresponding Tk widget has already been created by Tix diff --git a/Misc/NEWS.d/next/Library/2022-09-25-20-42-33.gh-issue-73588.uVtjEA.rst b/Misc/NEWS.d/next/Library/2022-09-25-20-42-33.gh-issue-73588.uVtjEA.rst new file mode 100644 index 0000000..d8a0e69 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-09-25-20-42-33.gh-issue-73588.uVtjEA.rst @@ -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. -- cgit v0.12