Title: ttk.OptionMenu radiobuttons change variable value twice
Type: behavior Stage: patch review
Components: Tkinter Versions: Python 3.11, Python 3.10, Python 3.9, Python 3.8, Python 3.7, Python 3.6
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: epaine, fhdrsdg, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2021-09-10 07:13 by fhdrsdg, last changed 2021-09-11 14:45 by epaine.

Pull Requests
URL Status Linked Edit
PR 28291 open epaine, 2021-09-11 14:45
Messages (3)
msg401557 - (view) Author: fhdrsdg (fhdrsdg) Date: 2021-09-10 07:13
Found when trying to answer this SO question:

The ttk.OptionMenu uses radiobuttons for the dropdown menu, whereas the tkinter.OptionMenu uses commands. Both of them use `_setit` as command, presumably first used for tkinter and then copied to the ttk implementation. The `_setit` does two things: changing the associated `variable` and calling the associated callback function. This is needed for the tkinter.OptionMenu since commands don't support changing a `variable`.

However, for the ttk.OptionMenu an additional reference to the variable was added to the radiobutton following this issue: This was needed to group radiobuttons, but now leads to the variable being changed twice: once by the radiobutton and once through `_setit`. When tracing the variable this leads to the tracing callback being called twice on only one change of the OptionMenu.

The solution is to not use `_setit` for the radiobutton command but instead just use `command=self._callback`
msg401567 - (view) Author: E. Paine (epaine) * Date: 2021-09-10 10:08
Thank you for reporting this issue (and doing your research!). I don't think we could use `command=self._callback` since this wouldn't pass the new value, but something like `command=lambda val=val: self._callback(val)` would work perfectly. Would you like to open a pull request for this (with accompanying news entry and test), or would you rather I did it?
msg401634 - (view) Author: fhdrsdg (fhdrsdg) Date: 2021-09-11 08:47
Ah yes, you're right that `command=self._callback` doesn't pass the new value like it should.
Your suggestion does, but throws a "TypeError: 'NoneType' object is not callable" when no command is given when creating the OptionMenu. The current `_setit` implementation uses a `if self.__callback:` check for that.

Should we do something like this?

for val in values:
    if self._callback:
        menu.entryconfigure('last', command=lambda val=val: self._callback(val))

I don't have any experience making pull requests. I might look into it at some point but for now it would be great if you could make it.
Date User Action Args
2021-09-11 14:45:20epainesetkeywords: + patch
stage: patch review
pull_requests: + pull_request26707
2021-09-11 08:47:08fhdrsdgsetmessages: + msg401634
2021-09-10 10:08:50epainesetnosy: + serhiy.storchaka, epaine
messages: + msg401567
2021-09-10 07:13:00fhdrsdgcreate