基于SQLite查询实现Tkinter ttk Combobox自动补全适配
整合SQLite数据加载与自动补全功能到ttk Combobox
我来帮你把自动补全功能和你现有的SQLite数据加载逻辑完美整合!下面是修改后的完整代码,我会给你拆解关键的改动点:
完整修改后的代码
from tkinter import ttk import sqlite3 class AutocompleteCombobox(ttk.Combobox): def set_completion_list(self, completion_list): """Use our completion list as our drop down selection menu, arrows move through menu.""" self._completion_list = sorted(completion_list, key=str.lower) # Work with a sorted list self._hits = [] self._hit_index = 0 self.position = 0 self.bind('<KeyRelease>', self.handle_keyrelease) self['values'] = self._completion_list # Setup our popup menu def autocomplete(self, delta=0): """autocomplete the Combobox, delta may be 0/1/-1 to cycle through possible hits""" 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 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) def handle_keyrelease(self, event): """event handler for the keyrelease event on this widget""" if event.keysym == "BackSpace": self.delete(self.index(INSERT), END) self.position = self.index(END) if event.keysym == "Left": if self.position < self.index(END): # delete the selection self.delete(self.position, END) else: self.position = self.position-1 # delete one character self.delete(self.position, END) if event.keysym == "Right": self.position = self.index(END) # go to end (no selection) if len(event.keysym) == 1: self.autocomplete() # No need for up/down, we'll jump to the popup # list at the position of the autocompletion def cb(x, sql_query, Var): global comboBox textfont = ("Helvetica", 14) # 替换普通Combobox为带自动补全的自定义类实例 comboBox = AutocompleteCombobox(x, font=textfont, width=15, textvariable=Var) comboBox.pack(anchor='e', pady=5) update(sql_query) # 更新自动补全列表 def update(sql_query): lis = [] def query(): nonlocal sql_query mydb = sqlite3.connect('Assets/backend/Data.db') cursor = mydb.cursor() cursor.execute(sql_query) a = cursor.fetchall() # 提取SQL查询结果中每个元组的第一个元素,转为纯字符串列表 lis.extend([item[0] for item in a]) mydb.close() query() # 调用自动补全类的方法设置列表,替代直接赋值values comboBox.set_completion_list(lis) # 测试示例(可根据你的界面逻辑调整) if __name__ == '__main__': import tkinter as tk root = tk.Tk() var = tk.StringVar() # 替换为你的SQL查询语句,比如"SELECT column_name FROM your_table" cb(root, "SELECT name FROM test_table", var) root.mainloop()
关键改动说明
- 直接复用自动补全类:原有的
AutocompleteCombobox类已经实现了完整的自动补全逻辑,我们不需要修改它的核心代码,直接拿来用就行。 - 替换Combobox实例:在
cb函数里,把原来创建普通ttk.Combobox的代码改成创建AutocompleteCombobox实例,这样控件就拥有了自动补全能力。 - 处理SQL查询结果:SQLite的
fetchall()返回的是元组列表(比如[("苹果",), ("香蕉",)]),我们需要提取每个元组里的目标元素,转换成纯字符串列表,这样自动补全的匹配逻辑才能正常工作。 - 使用自动补全设置方法:不再直接给
comboBox['values']赋值,而是调用set_completion_list方法,这个方法不仅会设置下拉选项,还会自动绑定触发自动补全的键盘事件。
小建议
- 尽量减少全局变量:我暂时保留了你原来的
global comboBox写法,但实际项目中可以考虑把comboBox作为cb函数的返回值,再传入update函数,这样代码结构会更清晰规范。 - 适配多列查询:如果你的SQL查询返回多列,只需要调整
[item[0] for item in a]中的索引,选择你要用于自动补全的列即可。
内容的提问来源于stack exchange,提问作者Ahmed Hassan




