How to detect when an OptionMenu or Checkbutton change?
Asked Answered
M

6

15

My tkinter application has several controls, and I'd like to know when any changes occur to them so that I can update other parts of the application.

Is there anything that I can do short of writing an updater function, and looping at the end with:

root.after(0, updaterfunction) 

This method has worked in the past but I'm afraid that it might be expensive if there are many things to check on.

Even if I did use this method, could I save resources by only updating items with changed variables? If so, please share how, as I'm not sure how to detect specific changes outside of the update function.

Midship answered 15/3, 2011 at 6:15 Comment(0)
T
22

Many tkinter controls can be associated with a variable. For those you can put a trace on the variable so that some function gets called whenever the variable changes.

Example:

In the following example the callback will be called whenever the variable changes, regardless of how it is changed.

def callback(*args):
    print(f"the variable has changed to '{var.get()}'")

root = tk.Tk()
var = tk.StringVar(value="one")
var.trace("w", callback)

For more information about the arguments that are passed to the callback see this answer

Thibodeau answered 15/3, 2011 at 12:52 Comment(1)
URL no longer validAlphabetic
K
9

If you are using a Tkinter Variable class like StringVar() for storing the variables in your Tkinter OptionMenu or Checkbutton, you can use its trace() method.

trace(), basically, monitors the variable when it is read from or written to.

The trace() method takes 2 arguments - mode and function callback.

trace(mode, callback)

  • The mode argument is one of “r” (call observer when variable is read by someone), “w” (call when variable is written by someone), or “u” (undefine; call when the variable is deleted).
  • The callback argument is the call you want to make to the function when the variable is changed.

This is how it is used -

def callback(*args):
    print("variable changed!")

var = StringVar()
var.trace("w", callback)
var.set("hello")

Source : https://dafarry.github.io/tkinterbook/variable.htm

Kare answered 22/6, 2020 at 16:13 Comment(0)
B
8

To have an event fired when a selection is made set the command option for OptionMenu

ex.

def OptionMenu_SelectionEvent(event): # I'm not sure on the arguments here, it works though
    ## do something
    pass

var = StringVar()
var.set("one")
options = ["one", "two", "three"]
OptionMenu(frame, var, *(options), command = OptionMenu_SelectionEvent).pack()
Branchiopod answered 7/3, 2016 at 2:17 Comment(6)
OptionMenu can't have a command linked to it that executes some other function in the way that a button object can have a command linked to it. OptionMenu just basically allows the user to set some variable to a predefined list (i.e. the Menu items of the OptionMenu), it then doesn't fire off some other action if you slap a "command = xxx" on it. To trigger an action off changing the selected option, you need to use a trace like Bryan states above.Guardafui
Another approach optionMenu.bind("<Configure>", optionMenuChanged)Lordship
@Guardafui Did you actually try it? It works when I use it. Note that it has to come after the options tuple. Maybe it matters Python 2 vs 3... Note also that it doesn't send an event to the called function, it sends the string value that it sets the variable to.Anatolian
Except that the argument to OptionMenu_SelectionEvent is not an event object, but is unfortunately just a string of the current selection. @Lordship I wouldn't recommend using .bind( '<Configure>'; as that only works because the optionMenu happens to [usually] change size when selecting some options, it will fail when changing between options that happen to have the same width.Lundeen
The event returns the text of the selected item. If the OP wants to track the index of the item for example, an extra step is required that will look for the string inside options and return its index. This is by no means perfect, since theoretically such a menu can have one or multiple same values. In practice doesn't make sense but it can occur if for example the menu is populated with values from a configuration file. All of this means that a custom options menu or completely custom widget is required. For most cases though getting the string of the selected item is enough.Herakleion
This is easier than the trace approach in the other answers and has equivalent functionality.Adversaria
J
0

This will print the dropdown selection to the console. but my suggestion is to avoid console in GUI based applications. create a text indicator and print output to it

use the function in below code

from tkinter import *
tk = Tk()
def OptionMenu_SelectionEvent(event):
    print(var.get())
    pass
var = StringVar(); var.set("one")
options = ["one", "two", "three"]
OptionMenu(tk, var, *(options), command = OptionMenu_SelectionEvent).pack()
tk.mainloop()
Jataka answered 17/6, 2020 at 6:19 Comment(1)
Copy entire code and try again, it's working and the command argument is also correct @BlackJackJataka
A
0

If you need to check the status of the checkbox and perform some action, just add a function to it, which will check via get(). Using the example of custom tkinter (in the usual case, another name for the widget):

...
def check():
    check=checkbox.get()
    if check==1:
        print("on")
    if check==0:
        print("off")

checkbox=CTkCheckBox(command=check)
...
Arlinearlington answered 7/5, 2024 at 11:42 Comment(0)
E
0

The command= option on the OptionMenu widget worked for me, Although for some reason I needed a parameter even though none is needed and there is no bound event.

# update the label on some widgets
def doNameCombo(event=None):
    updateCheckbutton['text']=f'Update\n{(name:=gameName.get())}' +' history?'
    fileMenu.entryconfig(0,label=f'Restore {name} history')
    histButton['text'] = f'Show {name} history'

gameName=StringVar() 
gNames=[game[1].gName for game in gamesDict.items()] #collect the names for the optionMenu
startGameindex=gNames.index('Octordle') 
gameName.set(gNames[startGameindex])# set the active choice
nameCombo=OptionMenu(histOptionFrame,gameName,command=doNameCombo,*gNames)
nameCombo.grid(row=0,column=0,sticky=W)
Edelstein answered 10/6, 2024 at 15:36 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.