如何在能够输入的同时将条目限制为 AutocompleteCombobox 中的可用选项?

How to restrict entry to only available options in AutocompleteCombobox while being able to type in it?

我正在使用 ttkwidgets.autocomplete 中的 AutocompleteCombobox 作为我的选择小部件。虽然所有功能都很好(比如能够在输入时过滤列表),但我希望能够输入它但只能从可用选项中输入,即我不应该输入自定义值。 我尝试使用 state=readonly 但它不允许我输入组合框。 任何解决方案将不胜感激。

由于您没有提供示例代码并且 tkinter 没有提供默认的自动完成组合,我假设您使用的是来自 ttkwidgets.autocomplete.

AutocompleteCombobox

要仅获取有效条目(无自定义条目),您必须 re-implement AutocompleteCombobox class.
的自动完成方法 逻辑很简单:检查当前用户输入是否在自动完成列表中。如果不是,删除最后一个字符并再次显示最后一个自动完成建议。

我使用此 source 中的示例代码作为我回答的基础。

这是实现自定义 MatchOnlyAutocompleteCombobox:

的代码片段
from tkinter import *

from ttkwidgets.autocomplete import AutocompleteCombobox

countries = [
    'Antigua and Barbuda', 'Bahamas', 'Barbados', 'Belize', 'Canada',
    'Costa Rica ', 'Cuba', 'Dominica', 'Dominican Republic', 'El Salvador ',
    'Grenada', 'Guatemala ', 'Haiti', 'Honduras ', 'Jamaica', 'Mexico',
    'Nicaragua', 'Saint Kitts and Nevis', 'Panama ', 'Saint Lucia',
    'Saint Vincent and the Grenadines', 'Trinidad and Tobago', 'United States of America'
]

ws = Tk()
ws.title('PythonGuides')
ws.geometry('400x300')
ws.config(bg='#8DBF5A')


class MatchOnlyAutocompleteCombobox(AutocompleteCombobox):

    def autocomplete(self, delta=0):
        """
        Autocomplete the Combobox.

        :param delta: 0, 1 or -1: how to cycle through possible hits
        :type delta: int
        """
        if delta:  # need to delete selection otherwise we would fix the current position
            self.delete(self.position, END)
        else:  # set position to end so selection starts where textentry ended
            self.position = len(self.get())
        # collect hits
        _hits = []
        for element in self._completion_list:
            if element.lower().startswith(self.get().lower()):  # Match case insensitively
                _hits.append(element)

        if not _hits:
            # No hits with current user text input
            self.position -= 1  # delete one character
            self.delete(self.position, END)
            # Display again last matched autocomplete
            self.autocomplete(delta)
            return

        # if we have a new hit list, keep this in mind
        if _hits != self._hits:
            self._hit_index = 0
            self._hits = _hits
        # only allow cycling if we are in a known hit list
        if _hits == self._hits and self._hits:
            self._hit_index = (self._hit_index + delta) % len(self._hits)
        # now finally perform the auto completion
        if self._hits:
            self.delete(0, END)
            self.insert(0, self._hits[self._hit_index])
            self.select_range(self.position, END)


frame = Frame(ws, bg='#8DBF5A')
frame.pack(expand=True)

Label(
    frame,
    bg='#8DBF5A',
    font=('Times', 21),
    text='Countries in North America '
).pack()

entry = MatchOnlyAutocompleteCombobox(
    frame,
    width=30,
    font=('Times', 18),
    completevalues=countries
)
entry.pack()

ws.mainloop()