简体   繁体   中英

Bind Tkinter Combobox to Entry change?

I have a ttk.Combobox that my users can select from a dropdown list of options, or manually type something in. I have it bound to Return , so that if my user presses return after making a change it will update, but if my user clicks in the box and accidentally types something else in, it will cause an error down the road. To be clear, I already have an event bound to a new selection, as well as pressing return.

I am asking if it is possible to check if the box value has been changed when focus leaves the box, and if so, then call a function? When I tried a FocusOut bind, everytime I click on one of the dropdowns it calls my function and doesn't let me select anything from the dropdown, so that isn't working.

selection.bind('<Return>', lambda event, entry=selection, row=row: update(
    updated_entry=entry.get(), row=row, entry=entry))
selection.bind('<<ComboboxSelected>>', lambda event, entry=selection, row=row: update(
    updated_entry=entry.get(), row=row, entry=entry))

edit: Here is a sample code. The way this is written, if the user selects an item from the dropdown, it updates the label. If the users types something in and presses Return, it updates the label. But if the user types something in, and clicks on the other dropdown, it does not update the label.

import tkinter as tk
from tkinter import ttk

def update(updated_entry, row, entry):
    label = tk.Text(root, height=1, width=10)
    label.insert(tk.END, updated_entry)
    label.grid(row=row, column=2)
    return 'break'

def gui(root):
    root.geometry('300x150')
    root.config(background='snow3')

    for row in range(2):
        options = ['test', 'test1', 'test2']
        selection = tk.ttk.Combobox(root, value=options)
        selection.bind('<Return>', lambda event, entry=selection, row=row: update(
            updated_entry=entry.get(), row=row, entry=entry))
        selection.bind('<<ComboboxSelected>>', lambda event, entry=selection, row=row: update(
            updated_entry=entry.get(), row=row, entry=entry))
        selection.grid(row=row, column=1)

        label = tk.Text(root, height=1, width=10)
        label.grid(row=row, column=2)

if __name__ == '__main__':
    root = tk.Tk()
    gui(root)
    tk.mainloop()

ttk.Combobox es are a subclass of Entry widgets, which means that you can add validation to them in the same manner as you would to their base class. Namely by using the validate= and validatecommand= options Entry s support.

The reason to do this is because "validation" will allow the contents of the associated Combobox to be checked when it loses focus—ie your stated goal. This should work fine in conjunction with the bound event-handling you already have. The following code, which is similar to your minimal reproducible example, illustrates how do to something like that.

Note: This approach would also allow doing some real validation of the values the user has entered to prevent problems later on if they're invalid.

import tkinter as tk
from tkinter import ttk

def update(updated_entry, entry):
    ''' Combobox change Callback. '''
    entry.delete('1.0', tk.END)
    entry.insert(tk.END, updated_entry)

def gui(root):
    root.geometry('300x150')
    root.config(background='snow3')

    for row in range(2):
        text = tk.Text(root, height=1, width=10)  # Widget to be updated.
        text.grid(row=row, column=2)

        def check_okay(new_value, text=text):
            update(new_value, text)
            return True  # Note: accepts anything.

        combobox = ttk.Combobox(root, value=('test', 'test1', 'test2'),
                                validate='focusout',
                                validatecommand=(root.register(check_okay), '%P'))
        combobox.grid(row=row, column=1)

        combobox.bind('<Return>', lambda event, entry=combobox, text=text:
                                    update(entry.get(), entry=text))
        combobox.bind('<<ComboboxSelected>>', lambda event, entry=combobox, text=text:
                                                update(entry.get(), entry=text))

if __name__ == '__main__':
    root = tk.Tk()
    gui(root)
    tk.mainloop()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM