From 575a0fecfe967b455f186065f6806a32fe9d0dcf Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 23 Jun 2026 07:29:27 +0300 Subject: [PATCH] gh-101284: Allow passing Menubutton options to tkinter.OptionMenu 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 --- Doc/library/tkinter.rst | 5 ++++ Doc/whatsnew/3.16.rst | 4 ++++ Lib/test/test_tkinter/test_widgets.py | 17 ++++++++++++-- Lib/tkinter/__init__.py | 23 +++++++++++-------- ...-06-23-12-00-00.gh-issue-101284.Ab3Xy9.rst | 2 ++ 5 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-23-12-00-00.gh-issue-101284.Ab3Xy9.rst diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index 029121894656592..cee84d67668a84c 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 f394298da96a7cd..445be69128eb190 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 e388f0e8ed8f93d..4b7a6acfad0b922 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 f940216253dee1e..122f4da25de5a2a 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 000000000000000..9afcf8dcf545ed5 --- /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.