You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()

错误分析及修复方案

  1. prodnames()函数无返回值且未获取查询结果

    • 原函数只执行查询但没读取数据,也没返回内容,导致Combobox的value参数为None,无法加载选项。
    • 修复:用fetchall()获取所有产品名称,整理成列表返回,同时正确关闭数据库连接。
  2. save_invoice()中SQL语句错误

    • 创建invoices表时缺少item_price字段,但插入语句却引用该字段;且占位符数量和传入值数量不匹配(3个占位符对应5个值)。
    • 修复:修改表结构添加item_price字段,调整插入语句的占位符数量为对应值的数量。
  3. add_item()函数参数问题

    • 按钮的command参数无法直接传递自定义参数,原函数定义会导致调用报错。
    • 修复:用lambda表达式传递参数。
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 18:27:18