[Python] Python autocomplete popup: double mouse selection

Discussão em 'Python' iniciado por Stack, Agosto 26, 2025.

  1. Stack

    Stack Membro Participativo

    I am creating an autocomplete popup in Python using a Listbox which pops up. One can navigate by using arrows and using Enter to select or use the mouse to select an option. The arrow navigation is working perfectly; however, when I use the mouse to select an option and I want to use the arrows again, it automatically selects the second item on the list. Here is the code:

    import tkinter as tk

    masterlist = ["Daniel", "Ronel", "Dana", "Elani", "Isabel", "Hercules", "Karien", "Amor", "Piet", "Koos", "Jan", "Johan", "Denise", "Jean", "Petri"]
    global listbox_wiggie_index

    def new_list_for_listbox(*args):
    # When text in entry.wiggie is changed,
    # "search_var = tk.StringVar()" will change, which will activate this function.

    global listbox_wiggie_index

    listbox_wiggie_index = -1 # Reset index every time entry_wiggie changes so listbox is updated

    # Clean listbox_wiggie
    listbox_wiggie.delete(0, tk.END)

    # Get value of changeable search_var.
    t = search_var.get()

    if t == "":
    # Hide listbox_wiggie
    # If "listbox_wiggie.destroy()" is used, the widget will have to be created again
    listbox_wiggie.place_forget()
    return

    else:
    # Populate listbox_wiggie
    t = search_var.get()
    for e in masterlist:
    if t.lower() in e.lower():
    listbox_wiggie.insert(tk.END, e)

    place_listbox_wiggie()

    print(f"|new_list_for_listbox|New list was generated and listbox_wiggie was placed. Index = {listbox_wiggie_index}")

    def place_listbox_wiggie():
    # The listbox_wiggie should be already populated. It will be placed at the bottom of entry_wiggie

    # Get coordinates of bottom left of entry_wiggie.
    x = entry_wiggie.winfo_rootx() - root.winfo_rootx()
    y = entry_wiggie.winfo_rooty() - root.winfo_rooty() + entry_wiggie.winfo_height()

    # Place listbox_wiggie at the bottom of entry_wiggie
    listbox_wiggie.place(x=x, y=y)
    entry_wiggie.focus_set()

    def listbox_mouse_selection(event=None):
    # From: "listbox_wiggie.bind("<<ListboxSelect>>", listbox_mouse_selection)"
    i = listbox_wiggie.curselection()[0] # i = integer
    t = listbox_wiggie.get(i) # text of selection
    print("|listbox_mouse_selection| Selection was made using mouse")
    write_value_to_label(t)


    def write_value_to_label(teks):
    # Write selection to label_wiggie
    global listbox_wiggie_index

    label_wiggie.config(text=teks)
    # Hide listbox_wiggie
    listbox_wiggie.place_forget()
    entry_wiggie.focus_set()
    entry_wiggie.delete(0, tk.END)
    listbox_wiggie_index = -1
    print(f"|write_value_to_label|{teks} was written to label_wiggie")

    def change_listbox_selection_after_arrow_press():
    # Focus on listbox_wiggie after arrows are pressed
    global listbox_wiggie_index

    # Remove the selection highlight from every item in the Listbox, starting from index 0 to the last item (tk.END)
    listbox_wiggie.selection_clear(0, tk.END)
    # Select the new item according to the index "listbox_wiggie_index":
    listbox_wiggie.selection_set(listbox_wiggie_index)
    # Highlight the item as the active item:
    listbox_wiggie.activate(listbox_wiggie_index)
    print(f'|change_listbox_selection_after_arrow_press|Selector of listbox_wiggie was changed to = {listbox_wiggie_index}')

    def on_entry_wiggie_key_press(event):
    # Function is activated by: entry_wiggie.bind("<KeyRelease>", on_entry_wiggie_key_press)

    global listbox_wiggie_index

    if listbox_wiggie.size() > 0:
    if event.keysym == "Down":
    print(f'|on_entry_wiggie_key_press|Down was pressed. Before pressing, index = {listbox_wiggie_index}')
    listbox_wiggie_index += 1
    if listbox_wiggie_index >= listbox_wiggie.size():
    listbox_wiggie_index = 0
    change_listbox_selection_after_arrow_press()

    elif event.keysym == "Up":
    print('|on_entry_wiggie_key_press|Up was pressed. Before pressing, index = {listbox_wiggie_index}')
    listbox_wiggie_index -= 1
    if listbox_wiggie_index < 0:
    listbox_wiggie_index = listbox_wiggie.size() - 1
    change_listbox_selection_after_arrow_press()

    elif event.keysym == "Return":
    print('|on_entry_wiggie_key_press|Return was pressed.')
    # check to ensure listbox_wiggie_index is valid before calling listbox_wiggie.get()
    if 0 <= listbox_wiggie_index < listbox_wiggie.size():
    write_value_to_label(listbox_wiggie.get(listbox_wiggie_index))

    elif event.keysym == "Escape":
    print('|on_entry_wiggie_key_press|Escape was pressed.')
    # Clear the listbox
    listbox_wiggie.delete(0, tk.END)
    listbox_wiggie.place_forget()
    entry_wiggie.focus_set()

    else:
    print(f'|on_entry_wiggie_key_press|{event.keysym} was pressed.')
    listbox_wiggie_index = -1

    else:
    print(f'|on_entry_wiggie_key_press|{event.keysym} was pressed.')
    listbox_wiggie_index = -1

    print(f'|on_entry_wiggie_key_press|search_var = "{search_var.get()}". Focus = {root.focus_get()}. Indeks = {listbox_wiggie_index}. ...curselection()[0] = {listbox_wiggie.curselection()}')
    print()

    root = tk.Tk()
    root.title("Listbox appear after typing")
    root.geometry("400x200")

    # search_var is a variable which holds the content of entry_wiggie.
    search_var = tk.StringVar()
    search_var.trace("w", new_list_for_listbox)
    # new_list_for_listbox will only be activated when search_var is changed (when contents of entry_wiggie is changed)

    # Create entry_wiggie. Link content to StringVar "search_var"
    entry_wiggie = tk.Entry(root, textvariable=search_var, width=60)
    entry_wiggie.pack(pady=5)
    entry_wiggie.focus_set()
    entry_wiggie.bind("<KeyRelease>", on_entry_wiggie_key_press)

    # Create listbox_wiggie, but do not place it (yet)
    listbox_wiggie = tk.Listbox(root)
    listbox_wiggie.bind("<<ListboxSelect>>", listbox_mouse_selection)
    listbox_wiggie_index = -1

    # Create label_wiggie to display result
    label_wiggie = tk.Label(root, text="No option is selected.")
    label_wiggie.pack(pady=5)

    root.mainloop()

    Continue reading...

Compartilhe esta Página