解决Tkinter跨文件调用时ticket_price属性不存在的错误
问题描述
我开发了一个基于Tkinter的TicketSystem票务系统类,单独运行该类代码时功能正常。但在另一个主程序文件中调用该类后,点击“Calculate Total”按钮计算总价时,触发“tkinter has no attribute ticket_price”错误,请求协助解决该问题。
代码片段
TicketSystem类代码
import tkinter as tk from tkinter import ttk, messagebox import os import pandas as pd import random import string class TicketSystem(tk.Tk): def __init__(self): super().__init__() self.title("Ticket System") self.geometry("1200x800") # 数据库文件路径 self.database_filename = os.path.join(os.getcwd(), r"C:\Users\M S I\Downloads\Wisatasolo\database_tiket.xlsx") if not os.path.exists(self.database_filename): df = pd.DataFrame(columns=["Name", "Date", "People", "Total Cost", "Resi Code", "Payment Method"]) df.to_excel(self.database_filename, index=False) self.create_main_widgets() def create_main_widgets(self): self.clear_window() self.label = tk.Label(self, text="Pilih Opsi:") self.label.pack(pady=10) self.reschedule_button = tk.Button(self, text="Reschedule", command=self.reschedule) self.reschedule_button.pack(pady=5) self.refund_button = tk.Button(self, text="Refund", command=self.refund) self.refund_button.pack(pady=5) self.history_button = tk.Button(self, text="Riwayat Pembelian", command=self.view_history) self.history_button.pack(pady=5) self.book_ticket_button = tk.Button(self, text="Book Ticket", command=self.book_ticket) self.book_ticket_button.pack(pady=5) def return_funds(self): # 退款逻辑:返还80%款项 messagebox.showinfo("Success", "Dana kembali sebesar 80% dari pembayaran. Refund berhasil") self.create_main_widgets() def view_history(self): self.clear_window() # 显示购票历史逻辑 history = "Menampilkan riwayat pembelian..." self.label = tk.Label(self, text=history) self.label.pack(pady=10) self.back_button = tk.Button(self, text="Back", command=self.create_main_widgets) self.back_button.pack(pady=5) def book_ticket(self): self.clear_window() self.attractions = [ "Solo Safari", "Lokananta", "Tumurun", "Keraton Mangkunegaran", "Balekambang" ] self.selected_attraction = tk.StringVar() self.attraction_label = tk.Label(self, text="Select Attraction:") self.attraction_label.pack(pady=10) self.attraction_combo = ttk.Combobox(self, textvariable=self.selected_attraction, values=self.attractions) self.attraction_combo.pack(pady=10) self.attraction_combo.bind("<<ComboboxSelected>>", self.display_info) self.info_label = tk.Label(self, text=" ", wraplength=500) self.info_label.pack(pady=10) self.data_label = tk.Label(self, text="Enter your details:") self.data_label.pack(pady=10) self.name_label = tk.Label(self, text="Name:") self.name_label.pack(pady=5) self.name_entry = tk.Entry(self) self.name_entry.pack(pady=5) self.date_label = tk.Label(self, text="Date (dd-mm-yyyy):") self.date_label.pack(pady=5) self.date_entry = tk.Entry(self) self.date_entry.pack(pady=5) self.people_label = tk.Label(self, text="Number of People:") self.people_label.pack(pady=5) self.people_entry = tk.Entry(self) self.people_entry.pack(pady=5) self.payment_label = tk.Label(self, text="Payment Method:") self.payment_label.pack(pady=10) self.payment_methods = ["Credit Card", "Debit Card", "PayPal"] self.selected_payment = tk.StringVar() self.payment_combo = ttk.Combobox(self, textvariable=self.selected_payment, values=self.payment_methods) self.payment_combo.pack(pady=10) self.calculate_button = tk.Button(self, text="Calculate Total", command=self.calculate_total) self.calculate_button.pack(pady=20) self.total_label = tk.Label(self, text=" ") self.total_label.pack(pady=10) self.back_button = tk.Button(self, text="Back", command=self.create_main_widgets) self.back_button.pack(pady=5) self.print_button = tk.Button(self, text="Print Ticket", command=self.print_ticket) self.print_button.pack(pady=10) self.back_button = tk.Button(self, text="Back", command=self.create_main_widgets) self.back_button.pack(pady=5) def display_info(self, event): attraction = self.selected_attraction.get() info_text = " " if attraction == "Solo Safari": info_text = "Solo Safari: Rp50,000 per person" self.ticket_price = 50000 elif attraction == "Lokananta": info_text = "Lokananta: Rp30,000 per person" self.ticket_price = 30000 elif attraction == "Tumurun": info_text = "Tumurun: Rp25,000 per person" self.ticket_price = 25000 elif attraction == "Keraton Mangkunegaran": info_text = "Keraton Mangkunegaran: Rp40,000 per person" self.ticket_price = 40000 elif attraction == "Balekambang": info_text = "Balekambang: Rp35,000 per person" self.ticket_price = 35000 self.info_label.config(text=info_text) def calculate_total(self): try: self.name = self.name_entry.get().strip() self.date = self.date_entry.get().strip() self.people = int(self.people_entry.get()) self.payment_method = self.selected_payment.get() self.total_cost = self.people * self.ticket_price self.resi_code = self.generate_resi() # 保存订单到Excel self.save_resi_to_excel(self.name, self.date, self.people, self.total_cost, self.resi_code, self.payment_method) self.total_label.config(text=f"Total Cost: Rp{self.total_cost}\nResi Code: {self.resi_code}") messagebox.showinfo("Booking Successful", f"Name: {self.name}\nDate: {self.date}\nPeople: {self.people}\nPayment Method: {self.payment_method}\nTotal Cost: Rp{self.total_cost}\nResi Code: {self.resi_code}") except ValueError as ve: messagebox.showerror("Input Error", str(ve)) except Exception as e: messagebox.showerror("Error", str(e)) def generate_resi(self): resi_code = ''.join(random.choices(string.ascii_uppercase + string.digits, k=8)) return resi_code
主程序文件代码
import tkinter as tk from tkinter import Frame, Button from PIL import Image, ImageTk def show_main(username=None): root.withdraw() global window window = tk.Toplevel(root) window.title("Selamat Datang") window.geometry("1200x800") window.configure(bg="White") window.resizable(True, True) new_bg_image = Image.open(r"C:\Users\M S I\Downloads\Wisatasolo\Sapa.png") new_bg_image = new_bg_image.resize((1200, 800), Image.Resampling.LANCZOS) new_photo = ImageTk.PhotoImage(new_bg_image) background_label = tk.Label(window, image=new_photo) background_label.place(x=0, y=0, relwidth=1, relheight=1) background_label.image = new_photo new_frame = Frame(window, width=350, height=100, bg="#354f00") new_frame.place(x=600, y=600) Button(new_frame, width=38, height=1, text="Lanjutkan",command=back_login2, fg="white", bg="Black").place(x=40, y=20) Button(new_frame, width=38, height=1, text="Logout", fg="white", bg="#FF0000", command=back_login).place(x=40, y=50) def new_background(event=None): width = window.winfo_width() height = window.winfo_height() resized_image = new_bg_image.resize((width, height), Image.Resampling.LANCZOS) new_photo = ImageTk.PhotoImage(resized_image) background_label.config(image=new_photo) background_label.image = new_photo window.bind("<<Configure>>", new_background) window.mainloop() def back_login(): window.destroy() root.deiconify() def back_login2(): window.destroy() instance_kelas = TicketSystem()
问题原因与解决方案
核心原因
self.ticket_price仅在用户通过下拉框选择景点(触发display_info方法)时才会被赋值。如果用户未选择任何景点就点击“Calculate Total”,或者下拉框的绑定事件未正常触发,就会导致该属性未定义,从而抛出错误。单独运行时可能因为操作习惯(先选景点再计算)没触发,但主程序调用时的交互流程更容易出现这个问题。
修复方案
方案1:提前初始化默认值(最稳妥)
在book_ticket方法开头添加一行代码,提前初始化self.ticket_price:
def book_ticket(self): self.clear_window() self.ticket_price = 0 # 初始化默认值,选择景点后会自动覆盖 # ... 后续原有代码
方案2:添加输入校验
在calculate_total方法开头增加校验逻辑,确保景点已选择:
def calculate_total(self): try: # 先检查是否选择了景点 if not hasattr(self, 'ticket_price') or self.ticket_price == 0: messagebox.showerror("错误", "请先选择景点!") return # ... 后续原有代码
方案3:设置下拉框默认选中项
给景点下拉框设置默认选中第一个选项,并手动触发一次display_info,提前赋值self.ticket_price:
def book_ticket(self): self.clear_window() # ... 原有创建attractions列表的代码 self.attraction_combo = ttk.Combobox(self, textvariable=self.selected_attraction, values=self.attractions) self.attraction_combo.pack(pady=10) self.attraction_combo.bind("<<ComboboxSelected>>", self.display_info) # 添加以下两行 self.selected_attraction.set(self.attractions[0]) self.display_info(None) # ... 后续原有代码
额外优化建议
book_ticket方法中重复创建了两个back_button,删除其中一个即可避免冗余。- 数据库路径拼接存在错误:
os.path.join(os.getcwd(), r"C:\Users\...")会生成无效路径,直接写绝对路径即可:
self.database_filename = r"C:\Users\M S I\Downloads\Wisatasolo\database_tiket.xlsx"
内容的提问来源于stack exchange,提问作者Satrio Brahmantoro Adi Subagio
相关产品推荐
相关产品推荐

