为基于Tkinter+Treeview+SQLite3的库存CRUD程序添加搜索功能
实现Treeview搜索功能的解决方案
我来帮你搞定这个搜索功能!下面是具体的修改步骤和完整代码,你只需要把这些内容整合到你的现有程序里就行:
1. 添加搜索UI组件
首先我们在界面上添加一个搜索框和搜索按钮,放在消息提示区和Treeview之间,这样用户操作起来很直观。
新增一个create_search_bar方法:
def create_search_bar(self): # 创建搜索框架 search_frame = LabelFrame(self.root, text='Search Items', bg='sky blue', font='helvetica 10') search_frame.grid(row=4, column=0, columnspan=3, padx=8, pady=8, sticky='ew') # 搜索标签和输入框 Label(search_frame, text='Item Name:', bg='sky blue', fg='black').grid(row=0, column=0, sticky=W, pady=2, padx=15) self.search_entry = Entry(search_frame, width=30) self.search_entry.grid(row=0, column=1, sticky=W, padx=5, pady=2) # 搜索按钮,绑定搜索函数 Button(search_frame, text='Search', command=self.search_items, fg='black').grid(row=0, column=2, sticky=E, padx=5, pady=2) # 清空搜索按钮,恢复显示所有物品 Button(search_frame, text='Show All', command=self.view_items, fg='black').grid(row=0, column=3, sticky=E, padx=5, pady=2)
然后在create_gui方法里调用这个方法,位置放在create_message_area之后:
def create_gui(self): self.create_left_icon() self.create_label_frame() self.create_message_area() self.create_search_bar() # 新增这一行 self.create_tree_view() self.create_scrollbar() self.create_bottom_buttons() self.view_items()
2. 实现搜索逻辑
接下来编写search_items方法,它会:
- 获取搜索框的输入内容
- 遍历数据库中所有物品,匹配名称(支持模糊匹配)
- 在Treeview中只显示匹配的物品,并自动选中第一个匹配项
- 如果没有匹配项,显示提示消息
def search_items(self): self.message['text'] = '' search_term = self.search_entry.get().strip() if not search_term: self.message['text'] = 'Please enter an item name to search' return # 清空Treeview现有内容 items = self.tree.get_children() for item in items: self.tree.delete(item) # 执行模糊搜索的SQL查询 query = 'SELECT * FROM items_list WHERE TipeBarang LIKE ? ORDER BY TipeBarang' parameters = ('%' + search_term + '%',) item_entries = self.execute_db_query(query, parameters) matched_items = [] for row in item_entries: item_id = self.tree.insert('', 0, text=row[1], values=(row[2], row[3])) matched_items.append(item_id) if matched_items: # 选中第一个匹配项 self.tree.selection_set(matched_items[0]) # 滚动到选中项 self.tree.see(matched_items[0]) self.message['text'] = f'Found {len(matched_items)} matching item(s)' else: self.message['text'] = 'No items found matching your search'
完整修改后的代码
把上面的新增内容整合到你的原代码中,完整代码如下:
from tkinter import Tk, Button, PhotoImage, Label, LabelFrame, W, E, N, S, Entry, END, StringVar, Scrollbar, Toplevel from tkinter import ttk import sqlite3 class Inventory: db_filename = 'stock.db' def __init__(self, root): self.root = root self.create_gui() ttk.style = ttk.Style() ttk.style.configure('Treeview', font=('helvetica',10)) ttk.style.configure('Treeview.Heading', font=('helvetica', 12, 'bold')) def execute_db_query(self, query, parameters=()): with sqlite3.connect(self.db_filename) as conn: cursor = conn.cursor() query_result = cursor.execute(query, parameters) conn.commit() return query_result def create_gui(self): self.create_left_icon() self.create_label_frame() self.create_message_area() self.create_search_bar() # 新增搜索栏 self.create_tree_view() self.create_scrollbar() self.create_bottom_buttons() self.view_items() def create_left_icon(self): photo = PhotoImage(file='./icons/logo.png') label = Label(image=photo) label.image = photo label.grid(row=0, column=0) def create_label_frame(self): labelframe = LabelFrame(self.root, text='Input New Items', bg='sky blue', font='helvetica 10') labelframe.grid(row=0, column=1, padx=8, pady=8, sticky='ew') Label(labelframe, text='Name of Item:', bg='sky blue', fg='black').grid(row=1, column=1, sticky=W, pady=2, padx=15) self.typefield = Entry(labelframe) self.typefield.grid(row=1, column=2, sticky=W, padx=5, pady=2) Label(labelframe, text='Stock Card ID:', bg='sky blue', fg='black').grid(row=2, column=1, sticky=W, pady=2, padx=15) self.cardfield = Entry(labelframe) self.cardfield.grid(row=2, column=2, sticky=W, padx=5, pady=2) Label(labelframe, text='Count:', bg='sky blue', fg='black').grid(row=3, column=1, sticky=W, pady=2, padx=15) self.countfield = Entry(labelframe) self.countfield.grid(row=3, column=2, sticky=W, padx=5, pady=2) Button(labelframe, text='Add', command=self.on_add_item_button_clicked, fg='black').grid(row=4, column=2, sticky=E, padx=5, pady=5) def create_message_area(self): self.message = Label(text='', fg='red') self.message.grid(row=3, column=1, sticky=W) # 新增的搜索栏创建方法 def create_search_bar(self): search_frame = LabelFrame(self.root, text='Search Items', bg='sky blue', font='helvetica 10') search_frame.grid(row=4, column=0, columnspan=3, padx=8, pady=8, sticky='ew') Label(search_frame, text='Item Name:', bg='sky blue', fg='black').grid(row=0, column=0, sticky=W, pady=2, padx=15) self.search_entry = Entry(search_frame, width=30) self.search_entry.grid(row=0, column=1, sticky=W, padx=5, pady=2) Button(search_frame, text='Search', command=self.search_items, fg='black').grid(row=0, column=2, sticky=E, padx=5, pady=2) Button(search_frame, text='Show All', command=self.view_items, fg='black').grid(row=0, column=3, sticky=E, padx=5, pady=2) def create_tree_view(self): self.tree = ttk.Treeview(height=10, columns=('card', 'stock'), style='Treeview') self.tree.grid(row=6, column=0, columnspan=3) self.tree.heading('#0', text='Name of Item', anchor=W) self.tree.heading('card', text='Stock Card ID', anchor=W) self.tree.heading('stock', text='Count', anchor=W) def create_scrollbar(self): self.scrollbar = Scrollbar(orient='vertical', command=self.tree.yview) self.scrollbar.grid(row=6, column=3, rowspan=10, sticky='sn') # 绑定滚动条与Treeview同步 self.tree.configure(yscrollcommand=self.scrollbar.set) def create_bottom_buttons(self): Button(text='Delete', command=self.on_delete_selected_button_clicked).grid(row=8, column=0, sticky=W, pady=10, padx=20) Button(text='Edit Stock', command=self.on_modify_selected_button_clicked).grid(row=8, column=1, sticky=W) def on_add_item_button_clicked(self): self.add_new_item() def on_delete_selected_button_clicked(self): self.message['text'] = '' try: self.tree.item(self.tree.selection())['values'][0] except IndexError as e: self.message['text'] = 'Select at least one item to be deleted' return self.delete_items() def on_modify_selected_button_clicked(self): self.message['text'] = '' try: self.tree.item(self.tree.selection())['values'][0] except: self.message['text'] = 'Select at least one item to be modified' return self.open_modify_window() def add_new_item(self): if self.new_items_validated(): query = 'INSERT INTO items_list VALUES(NULL, ?, ?, ?)' parameters = (self.typefield.get(), self.cardfield.get(), self.countfield.get()) self.execute_db_query(query, parameters) self.message['text'] = '{} has been added'.format(self.typefield.get()) self.typefield.delete(0, END) self.cardfield.delete(0, END) self.countfield.delete(0, END) self.view_items() else: self.message['text'] = 'All entry field must be filled' self.view_items() def new_items_validated(self): return len(self.typefield.get()) !=0 and len(self.cardfield.get()) != 0 and len(self.countfield.get()) != 0 def view_items(self): items = self.tree.get_children() for item in items: self.tree.delete(item) query = 'SELECT * FROM items_list ORDER BY TipeBarang' item_entries = self.execute_db_query(query) for row in item_entries: self.tree.insert('', 0, text=row[1], values=(row[2], row[3])) def delete_items(self): self.message['text']='' name = self.tree.item(self.tree.selection())['text'] query = 'DELETE FROM items_list WHERE TipeBarang = ?' self.execute_db_query(query, (name,)) self.message['text'] = '{} has been deleted'.format(name) self.view_items() def open_modify_window(self): name = self.tree.item(self.tree.selection())['text'] old_stock = self.tree.item(self.tree.selection())['values'][1] self.transient = Toplevel() self.transient.title('Update Stock') Label(self.transient, text='Name: ').grid(row=0, column=1) Entry(self.transient, textvariable=StringVar(self.transient, value=name), state='readonly').grid(row=0, column=2) Label(self.transient, text='Old Stock Count: ').grid(row=1, column=1) Entry(self.transient, textvariable=StringVar(self.transient, value=old_stock), state='readonly').grid(row=1, column=2) Label(self.transient, text='New Stock Count: ').grid(row=2, column=1) new_stock_number_entry_widget = Entry(self.transient) new_stock_number_entry_widget.grid(row=2, column=2) Button(self.transient, text='Update Item', command=lambda: self.update_stock(new_stock_number_entry_widget.get(), old_stock, name)).grid(row=3, column=2, sticky=E) self.transient.mainloop() def update_stock(self, newstock, old_stock, name): query = 'UPDATE items_list SET JumlahStok=? WHERE JumlahStok=? AND TipeBarang=?' parameters = (newstock, old_stock, name) self.execute_db_query(query, parameters) self.transient.destroy() self.message['text'] = '{} Stock Count has been updated'.format(name) self.view_items() # 新增的搜索方法 def search_items(self): self.message['text'] = '' search_term = self.search_entry.get().strip() if not search_term: self.message['text'] = 'Please enter an item name to search' return # 清空现有Treeview内容 items = self.tree.get_children() for item in items: self.tree.delete(item) # 模糊搜索SQL语句 query = 'SELECT * FROM items_list WHERE TipeBarang LIKE ? ORDER BY TipeBarang' parameters = ('%' + search_term + '%',) item_entries = self.execute_db_query(query, parameters) matched_items = [] for row in item_entries: item_id = self.tree.insert('', 0, text=row[1], values=(row[2], row[3])) matched_items.append(item_id) if matched_items: # 选中第一个匹配项并滚动到它 self.tree.selection_set(matched_items[0]) self.tree.see(matched_items[0]) self.message['text'] = f'Found {len(matched_items)} matching item(s)' else: self.message['text'] = 'No items found matching your search' if __name__ == "__main__": root = Tk() root.title("Inventory System") root.resizable(width=False, height=False) application = Inventory(root) root.mainloop()
额外优化说明
- 我还给滚动条加了
self.tree.configure(yscrollcommand=self.scrollbar.set),这样滚动条和Treeview能同步工作,之前的代码里漏掉了这一步。 - 搜索支持模糊匹配,输入部分名称也能找到对应的物品,比如输入“pen”能找到“Pen”、“Pencil”等包含该字符串的物品。
- 点击“Show All”按钮可以快速恢复显示所有库存物品。
这样修改后,你就可以在搜索框输入物品名称,点击搜索后,Treeview会只显示匹配的物品,并且自动选中第一个结果,方便你直接进行修改或删除操作啦!
内容的提问来源于stack exchange,提问作者Owenn Gimli
相关产品推荐
相关产品推荐

