https://github.com/python/cpython/commit/868d9a82cab556a8f5ad1f54e81d39dd325d3f52
commit: 868d9a82cab556a8f5ad1f54e81d39dd325d3f52
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-06-23T06:24:17Z
summary:

gh-101284: Allow passing Menubutton options to tkinter.OptionMenu (GH-151959)

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 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-06-23-12-00-00.gh-issue-101284.Ab3Xy9.rst
M Doc/library/tkinter.rst
M Doc/whatsnew/3.16.rst
M Lib/test/test_tkinter/test_widgets.py
M Lib/tkinter/__init__.py

diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst
index 02912189465659..cee84d67668a84 100644
--- a/Doc/library/tkinter.rst
+++ b/Doc/library/tkinter.rst
@@ -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)
diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst
index f394298da96a7c..445be69128eb19 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -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
 ---
 
diff --git a/Lib/test/test_tkinter/test_widgets.py 
b/Lib/test/test_tkinter/test_widgets.py
index e388f0e8ed8f93..4b7a6acfad0b92 100644
--- a/Lib/test/test_tkinter/test_widgets.py
+++ b/Lib/test/test_tkinter/test_widgets.py
@@ -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")
diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
index f940216253dee1..122f4da25de5a2 100644
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -4516,20 +4516,23 @@ def __init__(self, master, variable, value, *values, 
**kwargs):
         """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
index 00000000000000..9afcf8dcf545ed
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-06-23-12-00-00.gh-issue-101284.Ab3Xy9.rst
@@ -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.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to