python - entry.focus_set() 在 tkinter 中不工作

python - entry.focus_set() not working in tkinter

我有一个条目、一个列表框(下拉列表)和另一个列表框。每当在条目中键入超过 3 个字符时。查找完成列表并将其插入到下拉列表中,然后显示下拉列表。如果从下拉列表中选择了一个项目。它的值应该插入到列表框中并从下拉列表中删除,并且条目应该再次获得焦点。但事实并非如此。

这是我的代码:

from tkinter import *


class Autocomplete(Frame, object):
    def __init__(self, *args, **kwargs):
        super(Autocomplete, self).__init__(*args, **kwargs)
        self.text = StringVar()
        self.entry = Entry(self, textvariable=self.text)
        self.frame = Frame(self)
        self.listbox = Listbox(self.frame)
        self.dropdown = Listbox(self.frame)

    def build(self):
        self.text.trace("w", lambda name, index, mode: self._update_dropdown())
        self.entry.focus_set()
        self.entry.pack()
        self.frame.pack()
        self.listbox.grid(column=0, row=0, sticky=N)
        self.dropdown.bind("<<ListboxSelect>>", lambda event: self._select_entry())
        self.dropdown.grid(column=0, row=0, sticky=N)
        self.dropdown.grid_forget()
        return self

    def _shorten_dropdown(self, index):
        self.dropdown.grid_forget()
        self.dropdown.delete(index)
        self.dropdown["height"] -= 1
        self.dropdown.selection_clear(0, END)
        self.dropdown.grid(column=0, row=0, sticky=N)

    def _select_entry(self):
        index = int(self.dropdown.curselection()[0])
        value = self.dropdown.get(index)
        self._shorten_dropdown(index)
        self.entry.focus_set()

这是最少的代码。 Here 是可测试的版本。这是构建自动完成实例的代码:

from tkinter import *
from autocomplete import Autocomplete

listt = ["a","aa","aaa","ab","bba","aba","abbnb","cd","c","abc","abcd"]
root = Tk()

autocomplete_frame = Autocomplete(
    60,
    10,
    listt
).build()
autocomplete_frame.pack()

mainloop()

因此,我必须更改一些内容才能使其至少适用于一个词。目前,如果您希望在连续的字符串中继续输入更多单词,它将不起作用。

我相信我最终也能使它正常工作,但我正在尝试解决从列表框中更新单词并将焦点返回到输入字段的问题。

我不得不改变一些东西。

首先我必须改变:

index = int(self.dropdown.curselection()[0])

收件人:

index = self.dropdown.curselection()[0]

因为 self.dropdown.curselection()[0] 的返回值已经是一个 int。它不需要再次使整数成为整数。

其次我不得不改变:

def _add_course(self, value):
    self.listbox.insert(END, value)
    self.my_list.append(value)

收件人:

def _add_course(self, value):
    self.entry.delete(0,END)
    self.entry.insert(END, value)
    self.my_list.append(value)

当您尝试将 value 插入列表框而不是输入字段时。另请注意,我将 self.list 更改为 self.my_list`。您应该避免命名可能会覆盖内置函数的东西。

我需要更改的最后一件事是我们如何将焦点设置回输入字段。我相信焦点的问题在于您将焦点设置在鼠标单击上,但是您仍在单击列表框并且正在关注。我们需要为焦点设置添加延迟,以便我们有时间在焦点设置到输入字段之前完成鼠标单击。

变化:

self.entry.focus_set()

至:

self.master.after(200, lambda: self.entry.focus())

将焦点设置回输入字段的替代方法是 bind() 将按钮释放事件改为焦点命令。

你也可以这样强制对焦:

self.dropdown.bind("<ButtonRelease-1>", lambda x: self.entry.focus())

下面的代码应该可以解决您的主要问题:

from tkinter import *

class Autocomplete(Frame, object):
    def __init__(self, width, height, entries, *args, **kwargs):
        super(Autocomplete, self).__init__(*args, **kwargs)
        self.my_list = []
        self._entries = entries
        self.listbox_height = height
        self.entry_width = width
        self.text = StringVar()
        self.entry = Entry(self, textvariable=self.text, width=self.entry_width)
        self.frame = Frame(self)
        self.listbox = Listbox(self.frame, height=self.listbox_height, width=self.entry_width)
        self.dropdown = Listbox(self.frame, height=self.listbox_height, width=self.entry_width, background="#cfeff9")

    def build(self):
        self.text.trace("w", lambda name, index, mode: self._update_dropdown())
        self.entry.focus_set()
        self.entry.pack()
        self.frame.pack()
        self.listbox.grid(column=0, row=0, sticky=N)
        self.dropdown.bind("<<ListboxSelect>>", lambda event: self._select_entry())
        self.dropdown.grid(column=0, row=0, sticky=N)
        self.dropdown.grid_forget()
        return self

    def _update_dropdown(self):
        self.dropdown["height"] = self.listbox_height
        self.dropdown.delete(0, END)
        text = self.text.get()
        print("update: " + text)
        if len(text) < 3:
            self.dropdown.grid_forget()
            return
        else:
            for entry in self._entries:
                if entry not in self.my_list and text.lower() in entry.lower():
                    self.dropdown.insert(END, entry)
        listbox_size = self.dropdown.size()
        if not listbox_size:
            self.dropdown.insert(END, "No results found for '{}'".format(text))
            self.dropdown["height"] = 1
        else:
            if listbox_size <= self.dropdown["height"]:
                self.dropdown["height"] = listbox_size
        self.dropdown.grid(column=0, row=0, sticky=N)

    def _shorten_dropdown(self, index):
        print("shorten: {}".format(str(index)))
        self.dropdown.grid_forget()
        self.dropdown.delete(index)
        if self.dropdown["height"] == 1:
            self.dropdown.insert(END, "No more results found for '{}'".format(self.text.get()))
        else:
            self.dropdown["height"] -= 1
        self.dropdown.selection_clear(0, END)
        self.dropdown.grid(column=0, row=0, sticky=N)

    def _select_entry(self):
        index = self.dropdown.curselection()[0]
        value = self.dropdown.get(index)
        print(value)
        if "results found for" in value:
            print("return")
            return
        print("select: {}".format(value))
        self._shorten_dropdown(index)
        self._add_course(value)
        self.master.after(200, lambda: self.entry.focus())

    def _add_course(self, value):
        self.entry.delete(0,END)
        self.entry.insert(END, value)
        self.my_list.append(value)



listt = ["a","aa","aaa","ab","bba","aba","abbnb","cd","c","abc","abcd"]
root = Tk()

autocomplete_frame = Autocomplete(60, 10, listt).build()
autocomplete_frame.pack()

mainloop()