即使主窗口存在,组合框也会抛出错误

问题描述 投票:0回答:1

这是我的主要代码

import ttkbootstrap as ttk
from random import choice, choices
import csv
from Login import Login
login = Login()
login.login_gui()
user = login.authenticated_user
if user:
    with open('tanishStock_data.csv') as sheet:
        reader = list(csv.DictReader(sheet))
        day = int(reader[-1]['Day'])
        cash = float(reader[-1]['Cash'])
        stocks = {"reliance":float(reader[-4]['Price']), "tata motors":float(reader[-3]['Price']), "itc":float(reader[-2]['Price']), "mahindra":float(reader[-1]['Price'])}
    labels = {} #stores stock_name:it's price_label (refer to line 108)
    holdings = {} #stock : {quantity, price, value}
    def update_label(label, text, fg=None):
        label.configure(text=text, foreground = fg)
    portfolio_window = None
    def bull_run(stock):
        inc_or_dec = choices([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], weights=[20, 30, 10, 8, 7, 4, 3, 2, 2, 1])[0]
        stocks[stock] = stocks[stock] * inc_or_dec / 100 + stocks[stock]
    def bear_run(stock):
        inc_or_dec = choices([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], weights=[20, 30, 10, 8, 7, 4, 3, 2, 2, 1])[0]
        stocks[stock] = stocks[stock] - stocks[stock] * inc_or_dec / 100
    def simulate_stocks():
        for stock in stocks:
            randomizer = choice(["plus", "minus"])
            if randomizer == "plus":
                bull_run(stock)
            elif randomizer == "minus":
                bear_run(stock)
            else:
                print("error encountered")
                break
        update_prices()
        main_window.after(5000, simulate_stocks)
    def update_prices():
        for stock_name, label in labels.items():
            price = float(label.cget("text")) #gets the price out of the price_label
            if price > stocks[stock_name]:
                update_label(label, f"{stocks[stock_name]:.2f}", fg = "red")
            else:
                update_label(label, f"{stocks[stock_name]:.2f}", fg = "#228B22")
    def buy_stock():
        global cash
        stock = vvariable.get().strip().lower()
        try:
            quantity = int(entry_quantity_price.get())
        except ValueError:
            update_label(quantity_warning, "*Enter a valid quantity")
            return
        if stock in stocks:
            total_cost = quantity * stocks[stock]
            if total_cost > cash:
                update_label(quantity_warning, "*Not enough cash")
            elif quantity <= 0 or quantity > 10000:
                update_label(quantity_warning,"*Quantity should be between 1 and 10000")
            else:
                update_label(quantity_warning, "")
                update_label(stock_warning, "")
                if stock not in holdings:
                    holdings[stock] = {"quantity": 0, "price": 0, "value": 0}
                holdings[stock]["price"] = (stocks[stock] * quantity + holdings[stock]["price"] * holdings[stock]["quantity"])/(quantity + holdings[stock]["quantity"])
                holdings[stock]["quantity"] += quantity
                holdings[stock]["value"] = holdings[stock]["quantity"] * holdings[stock]["price"]
                cash -= total_cost
                update_label(cash_display, f"CASH: {cash:.2f}")
        else:
            update_label(stock_warning, "*No such stock exists")
    def sell_stock():
        global cash
        stock = vvariable.get().strip().lower()
        try:
            quantity = int(entry_quantity_price.get())
        except ValueError:
            update_label(quantity_warning, "*Enter a valid quantity")
            return
        if stock in holdings:
            if quantity <= 0 or quantity > holdings[stock]["quantity"]:
                update_label(quantity_warning, "*Invalid quantity")
            else:
                update_label(quantity_warning, "")
                update_label(stock_warning, "")
                holdings[stock]["quantity"] -= quantity
                sell_value = quantity * stocks[stock]
                cash += sell_value
                update_label(cash_display, f"CASH: {cash:.2f}")
                holdings[stock]["value"] = holdings[stock]["quantity"] * holdings[stock]["price"]
                if holdings[stock]["quantity"] == 0:
                    del holdings[stock]
        else:
            stock_warning.config(text="*You don't own this stock")
    def stock_data_writer(real_day): # writes stock data in csv file
        with open('tanishStock_data.csv', mode ='a', newline ='') as sheet:
            writer = csv.DictWriter(sheet, fieldnames = ['Day', 'Stock', 'Price', 'Cash'])
            for stock in stocks:
                writer.writerow({'Day' : real_day, 'Stock' : stock, 'Price' : f'{stocks[stock]:.2f}', 'Cash' : f'{cash:.2f}'})
    def day_system(): #manages day system in the stock simulator
        global day
        day += 1
        stock_data_writer(day)
        update_label(day_display, text = f"DAY : {day}", fg = "red")
        main_window.after(20000, lambda: day_system())
    def clickable_stocks(stockname, stockprice):
        entry_stock_entry.delete(0, ttk.END)
        entry_quantity_price.delete(0, ttk.END)
        entry_stock_entry.insert(0, stockname)
        entry_quantity_price.insert(0, stockprice)
    def show_portfolio():
        global portfolio_window

        # Destroy the existing portfolio window if it exists
        if portfolio_window:
            portfolio_window.destroy()

        # Create new portfolio window
        portfolio_window = ttk.Toplevel(main_window)
        portfolio_window.title("PORTFOLIO")
        portfolio_window.geometry("420x300")
        portfolio_window.resizable(False, False)

        # Create canvas and scrollbar
        canvas = ttk.Canvas(portfolio_window)
        scrollbar = ttk.Scrollbar(portfolio_window, orient="vertical", command=canvas.yview)
        canvas.configure(yscrollcommand=scrollbar.set)

        # Create a frame inside the canvas
        canvas_frame = ttk.Frame(canvas)
        canvas.create_window((0, 0), window=canvas_frame, anchor="nw")

        # Pack the scrollbar and canvas
        scrollbar.pack(side="right", fill="y")
        canvas.pack(side="left", fill="both", expand=True)

        def update_portfolio_window():
            # Clear the frame before updating
            for widget in canvas_frame.winfo_children():
                widget.destroy()
            row_num = 2
            cash_label = ttk.Label(canvas_frame, text=f"CASH: {cash:.2f}", font=("arial", 13, "bold"),foreground="green")
            cash_label.grid(row=0, column=0, padx=10, pady=10, sticky="w")
            upper_divider = ttk.Label(canvas_frame, text="_" * 80)
            upper_divider.grid(row=1, column=0, columnspan=2, padx=10)
            if holdings:
                for stock, data in holdings.items():
                    stock_name = ttk.Label(canvas_frame, text=stock.title(), font=("arial", 15))
                    purchase_data = ttk.Label(canvas_frame,
                                              text=f"Qty: {data['quantity']} | Price: {data['price']:.2f}",
                                              font=("arial", 8))
                    current_value = ttk.Label(canvas_frame, text=f"Value: {data['value']:.2f}", font=("arial", 13),foreground="green")
                    stock_name.grid(row=row_num, column=0, padx=10, pady=5, sticky="w")
                    purchase_data.grid(row=row_num + 1, column=0, padx=10, sticky="w")
                    current_value.grid(row=row_num + 1, column=1, padx=10, sticky="e")

                    row_num += 3
            else:
                no_holdings_label = ttk.Label(canvas_frame, text="No holdings yet.", font=("arial", 12))
                no_holdings_label.grid(row=row_num, column=0, columnspan=2, pady=50)
            close_button = ttk.Button(canvas_frame, text="Close", command=portfolio_window.destroy)
            close_button.grid(row=row_num + 1, column=1, sticky="e", padx=10, pady=10)
            canvas_frame.update_idletasks()
            canvas.config(scrollregion=canvas.bbox("all"))
        update_portfolio_window()
    main_window = ttk.Window(themename = "darkly")


    def on_destroy():
        print("Main window is being destroyed.")
        main_window.destroy()


    main_window.protocol("WM_DELETE_WINDOW", on_destroy)

    main_window.geometry("600x500")
    main_window.title("Tanish Stock Exchange")
    vvariable = ttk.StringVar()
    header = ttk.Label(main_window, text = "TSE", font=("arial", 20), foreground="blue")
    header.pack()
    day_display = ttk.Label(main_window, text = f"DAY : {day}", font = ("arial", 15), foreground='red')
    day_display.place(x = 260, y = 300)
    y_position = 80
    stock_entry = ttk.Label(main_window, text ="STOCK: ", font=("arial", 15, "bold"))
    stock_entry.place(x = 300, y = 80)
    stock_warning = ttk.Label(main_window, text = "", font=("arial", 7), foreground="red")
    stock_warning.place(x = 400, y=110)
    stock_quantity = ttk.Label(main_window, text ="quantity: ", font=("arial", 15, "bold"))
    stock_quantity.place(x = 300, y = 120)
    quantity_warning = ttk.Label(main_window, text = "", font=("arial", 7), foreground="red")
    quantity_warning.place(x = 400, y=150)
    entry_stock_entry = ttk.Combobox(master = main_window, values = ["reliance", "tata motors", "itc", "mahindra"], textvariable = vvariable)
    entry_stock_entry.place(x = 400, y = 83)
    entry_quantity_price = ttk.Entry(main_window)
    entry_quantity_price.place(x = 400, y = 125)
    buy = ttk.Button(main_window, text="BUY", style="success", width=7, padding=(20, 10), command = buy_stock)
    buy.place(x = 470, y = 170)
    sell = ttk.Button(main_window, text="SELL",style="danger", width=7, padding=(20, 10), command = sell_stock)
    sell.place(x = 370, y = 170)
    portfolio = ttk.Button(main_window, text="Portfolio",style = "primary",padding = (20, 10),command=show_portfolio)
    portfolio.place(x=20,y=20)
    cash_display = ttk.Label(main_window, text = f"CASH: {cash}", font=("arial", 13, "bold"),foreground="blue")
    cash_display.place(x=410, y=30)
    for stock in stocks:
        stock_label = ttk.Label(main_window, text=f"{stock.title()}:", font=("arial", 15, "bold"), foreground="#4682B4")
        stock_label.place(x=20, y=y_position)
        price_label = ttk.Label(main_window, text=f"{stocks[stock]}", font=("arial", 15), foreground="green")
        price_label.place(x=150, y=y_position)
        labels[stock] = price_label
        stock_label.bind("<Button-1>", lambda event, stockname=stock: clickable_stocks(stockname, round(cash//float(labels[stockname].cget("text")))))
        y_position += 40
    simulate_stocks()
    day_system()
    main_window.mainloop()

您可能想知道什么是登录类,所以这里是:

import ttkbootstrap as ttk
import json
import bcrypt
import os

class Login:
    def __init__(self, username=None, password=None):
        self.username = username
        self.password = password
        self.authenticated_user = False

    def __str__(self):
        return "Login class made for login purposes in every project."

    def display_message(self, message_label, message, color="red"):
        message_label.config(text=message, foreground=color)

    def authenticate_user(self, username, password, window):
        self.username = username.get().strip().lower()
        self.password = password.get().strip().encode("utf-8")

        self.display_message(self.login_message_label, "")

        try:
            with open('User_data.json', 'r+') as user_data:
                data = json.load(user_data)

                for user in data:
                    if user["username"] == self.username:
                        if bcrypt.checkpw(self.password, user["password"].encode("utf-8")):
                            self.display_message(self.login_message_label, "Login successful!", "green")
                            window.destroy()
                            self.authenticated_user = True
                            return
                        else:
                            self.display_message(self.login_message_label, "Invalid password.")
                            return
                salt = bcrypt.gensalt()
                hashed_pass = bcrypt.hashpw(self.password, salt).decode("utf-8")
                new_entry = {"username": self.username, "password": hashed_pass}
                data.append(new_entry)
                user_data.seek(0)
                json.dump(data, user_data, indent=4)
                user_data.truncate()
                self.create_user_stock_file(self.username)
                self.display_message(self.login_message_label, "User added successfully!", "green")
                window.destroy()
                self.authenticated_user = True
        except FileNotFoundError:
            self.display_message(self.login_message_label, "Server error")
        except json.JSONDecodeError:
            self.display_message(self.login_message_label, "Server error")

    def create_user_stock_file(self, username):
        filename = f"{username}stock.csv"
        if not os.path.exists(filename):
            with open(filename, 'w') as file:
                file.write("StockName,Quantity,Price\n")

    def login_gui(self):
        login_window = ttk.Window(themename="darkly")
        login_window.title("TSA Login")
        login_window.geometry("400x250")

        ttk.Label(login_window, text="Welcome to TSA", font=("arial", 17, "bold")).pack()

        ttk.Label(login_window, text="Username: ", font=("arial", 12)).place(x=20, y=50)
        login_username_entry = ttk.Entry(login_window, width=30)
        login_username_entry.place(x=120, y=47)

        ttk.Label(login_window, text="Password: ", font=("arial", 12)).place(x=20, y=100)
        login_password_entry = ttk.Entry(login_window, show="*", width=30)
        login_password_entry.place(x=120, y=97)

        self.login_message_label = ttk.Label(login_window, text="", font=("arial", 10))
        self.login_message_label.place(x=20, y=130)

        ttk.Button(login_window, text="Login", style="primary-outline", width=13, command=lambda: self.authenticate_user(login_username_entry, login_password_entry, login_window)).place(x=160, y=160)

        login_window.mainloop()

所以,当我运行主代码时,不要关注 json 或 csv 文件,我登录并成功登录,为了方便起见,我现在对 csv 文件进行了硬编码,所以当我运行代码时,它成功打开登录gui,成功登录后,窗口被销毁,我希望它运行之后的代码,但出现此错误

Traceback (most recent call last):
  File "D:\Users\SYSTEM H424\Desktop\Tanish\FUN\main.py", line 196, in <module>
    entry_stock_entry = ttk.Combobox(master = main_window, values = ["reliance", "tata motors", "itc", "mahindra"], textvariable = vvariable)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Users\SYSTEM H424\Desktop\Tanish\FUN\.venv\Lib\site-packages\ttkbootstrap\style.py", line 4941, in __init__
    func(self, *args, **kwargs)
  File "C:\Users\SYSTEM H424\AppData\Local\Programs\Python\Python312\Lib\tkinter\ttk.py", line 677, in __init__
    Entry.__init__(self, master, "ttk::combobox", **kw)
  File "D:\Users\SYSTEM H424\Desktop\Tanish\FUN\.venv\Lib\site-packages\ttkbootstrap\style.py", line 4960, in __init__
    ttkstyle = Bootstyle.update_ttk_widget_style(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Users\SYSTEM H424\Desktop\Tanish\FUN\.venv\Lib\site-packages\ttkbootstrap\style.py", line 5050, in update_ttk_widget_style
    builder_method(builder, widget_color)
  File "D:\Users\SYSTEM H424\Desktop\Tanish\FUN\.venv\Lib\site-packages\ttkbootstrap\style.py", line 1215, in create_combobox_style
    arrowsize=self.scale_size(12),
              ^^^^^^^^^^^^^^^^^^^
  File "D:\Users\SYSTEM H424\Desktop\Tanish\FUN\.venv\Lib\site-packages\ttkbootstrap\style.py", line 1116, in scale_size
    winsys = self.style.master.tk.call("tk", "windowingsystem")
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_tkinter.TclError: can't invoke "tk" command: application has been destroyed

大约 3 个小时后,我现在很无助,我不明白为什么会触发它,因为就像创建了 GUI 一样。我还尝试调试它,添加了打印语句,甚至使用了 .protocall() 但没有任何帮助,甚至聊天 gpt 说你的代码一切正常,问题艾米在于库本身,它建议我重建整个应用程序。我是一个初学者,重建它听起来太无聊和忙碌,所以如果可能的话任何人都可以解决我的问题。我以前有登录和登录界面,可以互相交换,但我放弃了它,希望解决错误

python user-interface tkinter ttkbootstrap
1个回答
0
投票

可能是

ttkbootstrap
的设计/实现问题。

建议:

  • 仅创建一个最初隐藏的主窗口
  • 使用
    ttk.Toplevel()
    而不是
    ttk.Window()
    作为登录窗口
  • 使用
    .wait_window()
    而不是
    .mainloop()
    等待登录窗口关闭
  • 登录成功则恢复主窗口

以下是一个简化的示例:

import ttkbootstrap as ttk

class Login:
    def __init__(self):
        self.authenticated_user = False

    def login_gui(self):
        login_window = ttk.Toplevel()  # used ttk.Toplevel() instead of ttk.Window()
        login_window.title('TSA Login')
        login_window.geometry('400x250')

        ttk.Button(login_window, text='Login', style='primary-outline', width=13,
                   command=lambda: self.authenticate_user(login_window)).place(x=160, y=160)

        login_window.wait_window() # used wait_window() instead of mainloop()

    def authenticate_user(self, window):
        window.destroy()
        self.authenticated_user = True

# create main window and hidden initially
main_window = ttk.Window(themename='darkly')
main_window.withdraw()

# show the login window
login = Login()
login.login_gui()

if login.authenticated_user:
    main_window.title('Tanish Stock Exchange')
    main_window.geometry('600x500')

    def on_destroy():
        print('Main window is being destroyed.')
        main_window.destroy()

    main_window.protocol('WM_DELETE_WINDOW', on_destroy)

    stock_entry = ttk.Label(main_window, text='STOCK:', font=('arial', 15, 'bold'))
    stock_entry.place(x=300, y=80)

    vvariable = ttk.StringVar()
    options = ['reliance', 'tata motors', 'itc', 'mahindra']
    entry_stock_entry = ttk.Combobox(main_window, values=options, textvariable=vvariable)
    entry_stock_entry.place(x=400, y=83)

    main_window.deiconify() # resume the main window
    main_window.mainloop()
© www.soinside.com 2019 - 2024. All rights reserved.