AttributeError报错及发票系统Combobox加载产品数据问题求助
解决发票系统中Combobox读取产品数据库及相关报错问题
问题说明
我正在开发一个发票系统,项目包含多个.py文件。其中一个页面支持用户添加产品并保存至sqlite3数据库。在另一个invoicing.py文件中,我希望将产品名称显示在Combobox中供用户选择,但无法正确实现两个文件的连接以读取products.db表的数据,同时出现报错:AttributeError: 'function' object has no attribute 'products'。
原代码
import tkinter import tkinter as tk import sqlite3 from tkinter import ttk def save_invoice(): conn = sqlite3.connect('invoices.db') c = conn.cursor() c.execute("""CREATE TABLE IF NOT EXISTS invoices ( invoice_number text, customer_name text, item_description text, item_quantity real, total real )""") invoice_number = invoice_number_entry.get() customer_name = customer_name_entry.get() item_description = item_cmb.get() item_price = float(item_price_entry.get()) item_quantity = float(item_quantity_entry.get()) #total = float(tree.total.get()) c.execute("""INSERT INTO invoices (invoice_number, customer_name, item_description, item_price, item_quantity) VALUES (?,?,?)""", (invoice_number, customer_name, item_description, item_price, item_quantity))#, total)) conn.commit() conn.close() status_lbl.config(text="Invoice saved successfully") def prodnames(): item = sqlite3.connect('products.db') i = item.cursor() i.execute("SELECT name FROM products") i.close() def clear_item(): item_quantity_entry.delete(0, tkinter.END) item_quantity_entry.insert(0, "1") item_cmb.delete(0, tkinter.END) def add_item(self, item_quantity_entry, item_description_entry, item_price_entry, tree, clear_item): quantity = int(item_quantity_entry.get()) items = item_description_entry.get() price = float(item_price_entry.get()) total = [quantity*price] invoice_items = [items, quantity, price, total] tree.insert('', 0, values=invoice_items) clear_item() app = tk.Tk() app.title("Invoicing System") invoice_create_label = tk.Label(text="Create a new invoice") invoice_create_label.grid(row=0, column=2) invoice_number_label = tk.Label(text="Invoice Number:") invoice_number_label.grid(row=2, column=0) invoice_number_entry = tk.Entry() invoice_number_entry.grid(row=3, column=0) customer_name_label = tk.Label(text="Customer Name:") customer_name_label.grid(row=2, column=1) customer_name_entry = tk.Entry() customer_name_entry.grid(row=3, column=1) item_description_label = tk.Label(text="Item Description:") item_description_label.grid(row=2, column=2) #item_description_entry = tk.Entry() #item_description_entry.grid(row=3, column=2) course=prodnames() item_cmb = ttk.Combobox(app, value=course) item_cmb.grid(row=3, column=2) item_price_label = tk.Label(text="Item Price:") item_price_label.grid(row=2, column=3) item_price_entry = tk.Entry() item_price_entry.grid(row=3, column=3) item_quantity_label = tk.Label(text="Item Quantity") item_quantity_label.grid(row=2, column=4) item_quantity_entry = tk.Entry() item_quantity_entry.grid(row=3, column=4) add_button = tk.Button(text="Add to invoice", command=add_item) add_button.grid(row=7, column=2) save_button = tk.Button(text="Save Invoice", command=save_invoice) save_button.grid(row=9, column=0) columns = ('items', 'price', 'quantity', 'total') tree = ttk.Treeview(app, columns=columns, show="headings") tree.heading('items', text='Items') tree.heading('price', text='Price') tree.heading('quantity', text='Quantity') tree.heading('total', text='Total') tree.grid(row=8, column=0, columnspan=5, padx=20, pady=10) status_lbl = tk.Label(text="") status_lbl.grid(row=9, column=1) app.mainloop()
错误分析及修复方案
prodnames()函数无返回值且未获取查询结果- 原函数只执行查询但没读取数据,也没返回内容,导致Combobox的
value参数为None,无法加载选项。 - 修复:用
fetchall()获取所有产品名称,整理成列表返回,同时正确关闭数据库连接。
- 原函数只执行查询但没读取数据,也没返回内容,导致Combobox的
save_invoice()中SQL语句错误- 创建
invoices表时缺少item_price字段,但插入语句却引用该字段;且占位符数量和传入值数量不匹配(3个占位符对应5个值)。 - 修复:修改表结构添加
item_price字段,调整插入语句的占位符数量为对应值的数量。
- 创建
add_item()函数参数问题- 按钮的
command参数无法直接传递自定义参数,原函数定义会导致调用报错。 - 修复:用lambda表达式传递参数。
- 按钮的
add_item()中total格式错误- 原代码把
total定义为列表,Treeview需要单个数值,会导致显示异常。 - 修复:直接计算为数值类型。
- 原代码把
修复后的完整代码
import tkinter import tkinter as tk import sqlite3 from tkinter import ttk def save_invoice(): conn = sqlite3.connect('invoices.db') c = conn.cursor() # 修复表结构,添加item_price字段 c.execute("""CREATE TABLE IF NOT EXISTS invoices ( invoice_number text, customer_name text, item_description text, item_price real, item_quantity real, total real )""") invoice_number = invoice_number_entry.get() customer_name = customer_name_entry.get() item_description = item_cmb.get() item_price = float(item_price_entry.get()) item_quantity = float(item_quantity_entry.get()) total = item_price * item_quantity # 计算总价 # 修复占位符数量,与值的数量匹配 c.execute("""INSERT INTO invoices (invoice_number, customer_name, item_description, item_price, item_quantity, total) VALUES (?,?,?,?,?,?)""", (invoice_number, customer_name, item_description, item_price, item_quantity, total)) conn.commit() conn.close() status_lbl.config(text="Invoice saved successfully") def prodnames(): conn = sqlite3.connect('products.db') c = conn.cursor() c.execute("SELECT name FROM products") # 获取所有产品名称,整理为列表 products = [row[0] for row in c.fetchall()] conn.close() # 正确关闭连接 return products # 返回产品列表 def clear_item(): item_quantity_entry.delete(0, tkinter.END) item_quantity_entry.insert(0, "1") item_cmb.delete(0, tkinter.END) item_price_entry.delete(0, tkinter.END) # 新增清空价格输入框 def add_item(item_quantity_entry, item_cmb, item_price_entry, tree, clear_item): try: quantity = int(item_quantity_entry.get()) items = item_cmb.get() price = float(item_price_entry.get()) total = quantity * price # 修复为数值类型 invoice_items = [items, price, quantity, total] # 调整顺序匹配Treeview列 tree.insert('', 0, values=invoice_items) clear_item() except ValueError: status_lbl.config(text="请输入有效的数字") app = tk.Tk() app.title("Invoicing System") invoice_create_label = tk.Label(text="Create a new invoice") invoice_create_label.grid(row=0, column=2) invoice_number_label = tk.Label(text="Invoice Number:") invoice_number_label.grid(row=2, column=0) invoice_number_entry = tk.Entry() invoice_number_entry.grid(row=3, column=0) customer_name_label = tk.Label(text="Customer Name:") customer_name_label.grid(row=2, column=1) customer_name_entry = tk.Entry() customer_name_entry.grid(row=3, column=1) item_description_label = tk.Label(text="Item Description:") item_description_label.grid(row=2, column=2) # 获取产品列表并设置Combobox选项 product_list = prodnames() item_cmb = ttk.Combobox(app, value=product_list) item_cmb.grid(row=3, column=2) item_price_label = tk.Label(text="Item Price:") item_price_label.grid(row=2, column=3) item_price_entry = tk.Entry() item_price_entry.grid(row=3, column=3) item_quantity_label = tk.Label(text="Item Quantity") item_quantity_label.grid(row=2, column=4) item_quantity_entry = tk.Entry() item_quantity_entry.insert(0, "1") # 默认设置数量为1 item_quantity_entry.grid(row=3, column=4) # 使用lambda传递参数给add_item add_button = tk.Button(text="Add to invoice", command=lambda: add_item(item_quantity_entry, item_cmb, item_price_entry, tree, clear_item)) add_button.grid(row=7, column=2) save_button = tk.Button(text="Save Invoice", command=save_invoice) save_button.grid(row=9, column=0) columns = ('items', 'price', 'quantity', 'total') tree = ttk.Treeview(app, columns=columns, show="headings") tree.heading('items', text='Items') tree.heading('price', text='Price') tree.heading('quantity', text='Quantity') tree.heading('total', text='Total') # 设置列宽,优化显示 tree.column('items', width=150) tree.column('price', width=80) tree.column('quantity', width=80) tree.column('total', width=80) tree.grid(row=8, column=0, columnspan=5, padx=20, pady=10) status_lbl = tk.Label(text="") status_lbl.grid(row=9, column=1) app.mainloop()
额外说明
- 确保
products.db数据库存在且包含products表,表中有name字段,否则需要先创建对应表和数据。 - 新增输入验证,防止非数字内容导致程序崩溃。
- 优化了界面元素的默认值和显示效果。
内容的提问来源于stack exchange,提问作者Kate Murphy
相关产品推荐
相关产品推荐

