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

解决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)
    # ... 后续原有代码

额外优化建议

  1. book_ticket方法中重复创建了两个back_button,删除其中一个即可避免冗余。
  2. 数据库路径拼接存在错误: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 01:39:50