0

I have a tkinter application with a ttk.OptionMenu whose value is set to a tk.StringVar. I would like to get a callback whenever the user picks a new option in the option menu. After registering a callback on the StringVar, whenever the user changes the option menu selection, the callback is fired twice. Is there any way to have the callback fire exactly once when the user makes a change to the menu?

Here's the complete (runnable) example:

import tkinter as tk
from tkinter import ttk


OPTIONS = ["A", "B", "C"]

# Set up a new window
root = tk.Tk()

# Create a variable
option_var = tk.StringVar()

# Register a callback that fires when the variable is changed
option_var.trace("w", lambda *args: print("option_var is", option_var.get(), "with args", args))

# Create the OptionMenu and add it to the GUI root
ttk.OptionMenu(root, option_var, OPTIONS[0], *OPTIONS).pack()

# Run the application
root.mainloop()

As expected, on startup, the callback fires once to reflect the change to the default variable:

option_var is A with args ('PY_VAR0', '', 'w')

However, if a user clicks the dropdown and selects B, the callback is fired twice.

option_var is B with args ('PY_VAR0', '', 'w')
option_var is B with args ('PY_VAR0', '', 'w')

Is there a way to set up the callback such that it only fires once when the option menu changes? Alternatively, is there some way to separate out one of these callbacks from the other so that I can take an action once per menu change?

(Example tested with CPython 3.6.8 running on Windows 10 1809)

ddulaney
  • 843
  • 7
  • 19

1 Answers1

2

There's no need to use trace here, the Optionmenu has a command argument:

import tkinter as tk
from tkinter import ttk

OPTIONS = ["A", "B", "C"]

root = tk.Tk()

def callback(value):
    print("option_var is", value)

option_var = tk.StringVar()
ttk.OptionMenu(root, option_var, OPTIONS[0], *OPTIONS, command=callback).pack()

root.mainloop()
Novel
  • 13,406
  • 2
  • 25
  • 41
  • Thanks! Is there anywhere in the docs where that's specified? Since effbot.org went down, it's been tricky to find out about the optional parameters each widget takes. – ddulaney Jan 23 '21 at 22:33
  • You can always use [the wayback machine](https://web.archive.org/web/20200704224617/http://effbot.org/tkinterbook/optionmenu.htm). There's other sites too, but usually I just open up the ttk.py file locally and look at the source code. – Novel Jan 23 '21 at 22:42
  • 1
    @ddulaney: the best source for documentation is the [tcl/tk man pages](http://www.tcl.tk/man). They are written assuming you're using tcl, but it's an easy mental translation into Python, and the [python docs have a section](https://docs.python.org/3/library/tkinter.html#mapping-basic-tk-into-tkinter) that discusses that very point. The good thing about the tcl/tk man pages is that they are authoritative and comprehensive. – Bryan Oakley Jan 23 '21 at 23:31