]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-101284: Allow passing Menubutton options to tkinter.OptionMenu (GH-151959)
authorSerhiy Storchaka <storchaka@gmail.com>
Tue, 23 Jun 2026 06:24:17 +0000 (09:24 +0300)
committerGitHub <noreply@github.com>
Tue, 23 Jun 2026 06:24:17 +0000 (06:24 +0000)
Arbitrary keyword arguments are now forwarded to the underlying
Menubutton and can override OptionMenu's default appearance options.
The positional API is unchanged.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Doc/library/tkinter.rst
Doc/whatsnew/3.16.rst
Lib/test/test_tkinter/test_widgets.py
Lib/tkinter/__init__.py
Misc/NEWS.d/next/Library/2026-06-23-12-00-00.gh-issue-101284.Ab3Xy9.rst [new file with mode: 0644]

index 029121894656592c746d63d7c5656ec2298afcf9..cee84d67668a84ca0b2885a8241b0807def03f8d 100644 (file)
@@ -4844,6 +4844,8 @@ Widget classes
    is the initial choice, and *values* are the remaining menu entries.
    The keyword argument *command* may be given a callback that is invoked with
    the selected value, and the keyword argument *name* sets the Tk widget name.
+   Other keyword arguments are passed to the underlying :class:`Menubutton`
+   and may override its default appearance.
 
    .. method:: destroy()
 
@@ -4852,6 +4854,9 @@ Widget classes
    .. versionchanged:: 3.14
       Added support for the *name* keyword argument.
 
+   .. versionchanged:: next
+      Other :class:`Menubutton` options can now be passed as keyword arguments.
+
 
 
 .. class:: PanedWindow(master=None, cnf={}, **kw)
index f394298da96a7cda90ef91e4fd2c42fe02320ba2..445be69128eb1905f1471c2bd2b0940d9f74ec5a 100644 (file)
@@ -216,6 +216,10 @@ tkinter
   dithered image when its data was supplied in pieces.
   (Contributed by Serhiy Storchaka in :gh:`151888`.)
 
+* :class:`tkinter.OptionMenu` now accepts arbitrary :class:`!tkinter.Menubutton`
+  options as keyword arguments, which can also override its default appearance.
+  (Contributed by Serhiy Storchaka in :gh:`101284`.)
+
 xml
 ---
 
index e388f0e8ed8f93d386efda879265f76ed3df83f7..4b7a6acfad0b922c14ff7257cfaeb7b121a04410 100644 (file)
@@ -437,9 +437,22 @@ class OptionMenuTest(MenubuttonTest, unittest.TestCase):
     def create(self, default='b', values=('a', 'b', 'c'), **kwargs):
         return tkinter.OptionMenu(self.root, None, default, *values, **kwargs)
 
+    def test_kwargs(self):
+        # Menubutton options can be passed at construction (gh-101284).
+        widget = tkinter.OptionMenu(self.root, None, 'b',
+                                    width=10, direction='right')
+        self.assertEqual(int(widget['width']), 10)
+        self.assertEqual(str(widget['direction']), 'right')
+        # They override OptionMenu's own appearance defaults,
+        widget = tkinter.OptionMenu(self.root, None, 'b', relief='flat')
+        self.assertEqual(str(widget['relief']), 'flat')
+        # which otherwise keep their historical values.
+        widget = tkinter.OptionMenu(self.root, None, 'b')
+        self.assertEqual(str(widget['relief']), 'raised')
+
     def test_bad_kwarg(self):
-        with self.assertRaisesRegex(TclError, r"^unknown option -image$"):
-            tkinter.OptionMenu(self.root, None, 'b', image='')
+        with self.assertRaisesRegex(TclError, r'^unknown option "-spam"$'):
+            tkinter.OptionMenu(self.root, None, 'b', spam='')
 
     def test_specify_name(self):
         widget = tkinter.OptionMenu(self.root, None, ':)', name="option_menu")
index f940216253dee1edae1a166f1286bea73fa14a9c..122f4da25de5a2aee9d6175f0f224d91a9656299 100644 (file)
@@ -4516,20 +4516,23 @@ class OptionMenu(Menubutton):
         """Construct an optionmenu widget with the parent MASTER, with
         the option textvariable set to VARIABLE, the initially selected
         value VALUE, the other menu values VALUES and an additional
-        keyword argument command."""
-        kw = {"borderwidth": 2, "textvariable": variable,
-              "indicatoron": 1, "relief": RAISED, "anchor": "c",
-              "highlightthickness": 2, "name": kwargs.pop("name", None)}
+        keyword argument command.
+
+        Other keyword arguments are passed to the underlying menubutton
+        and may override its default appearance."""
+        name = kwargs.pop("name", None)
+        callback = kwargs.pop("command", None)
+        # Default appearance, which may be overridden by keyword arguments.
+        kw = {"borderwidth": 2, "indicatoron": 1, "relief": RAISED,
+              "anchor": "c", "highlightthickness": 2}
+        kw.update(kwargs)
+        # These options are controlled by OptionMenu itself.
+        kw["textvariable"] = variable
+        kw["name"] = name
         Widget.__init__(self, master, "menubutton", kw)
         self.widgetName = 'tk_optionMenu'
         menu = self.__menu = Menu(self, name="menu", tearoff=0)
         self.menuname = menu._w
-        # 'command' is the only supported keyword
-        callback = kwargs.get('command')
-        if 'command' in kwargs:
-            del kwargs['command']
-        if kwargs:
-            raise TclError('unknown option -'+next(iter(kwargs)))
         menu.add_command(label=value,
                  command=_setit(variable, value, callback))
         for v in values:
diff --git a/Misc/NEWS.d/next/Library/2026-06-23-12-00-00.gh-issue-101284.Ab3Xy9.rst b/Misc/NEWS.d/next/Library/2026-06-23-12-00-00.gh-issue-101284.Ab3Xy9.rst
new file mode 100644 (file)
index 0000000..9afcf8d
--- /dev/null
@@ -0,0 +1,2 @@
+:class:`tkinter.OptionMenu` now accepts arbitrary :class:`!tkinter.Menubutton`
+options as keyword arguments and uses them to override its default appearance.