[Python] How to properly fill entry fields, and how to make the root appear just when is asked...

Discussão em 'Python' iniciado por Stack, Novembro 1, 2024.

  1. Stack

    Stack Membro Participativo

    So, I am creating a database, on Python (Tkinter). Everything was going well, untill I realized that when I try to autofill the entry field by selecting a product, these are not well populated. The problem is that, the entry fields are populated by what is on the treeview, I don't know how to make it so that the info is gathered from the JOSN file instead (The costs on the treeview are not shown at the same time, because there's 2 different, depending the one you select is the one shown and the application will calculate the gain and margin). The other thing is that I have a menu window, and I can't make the root not appear until the log in is complete, so you have 2 windows open and is not nice (this is more for the looks but still).I use python 3.13.0

    For the populated entry fields, I really don't know what to do, I am lost and have been scratching my head for the last few days.

    For the menu I tried using root.withdraw() at the end of the code, it works, but then when I complete the log in, the "database" (the main app) does not open.

    (If you see something I can change even if not related to the question I will be thankfull, Iam not a programming pro)

    import tkinter as tk
    from tkinter import messagebox, ttk
    import json
    import os

    class User:
    def __init__(self, username, password):
    self.username = username
    self.password = password

    def to_dict(self):
    return {
    "username": self.username,
    "password": self.password
    }

    @classmethod
    def from_dict(cls, data):
    return cls(data['username'], data['password'])

    @staticmethod
    def save_users(users):
    with open('users.json', 'w') as f:
    json.dump([user.to_dict() for user in users], f)

    @staticmethod
    def load_users():
    if os.path.exists('users.json'):
    with open('users.json', 'r') as f:
    return [User.from_dict(item) for item in json.load(f)]
    return []

    class Product:
    def __init__(self, name, price, cost_lb, cost_pm, weight, store_prices=None):
    self.name = name
    self.price = price
    self.cost_lb = cost_lb
    self.cost_pm = cost_pm
    self.weight = weight
    self.store_prices = store_prices if store_prices is not None else {}

    def gain(self, use_cost_lb=True):
    return self.price - (self.cost_lb if use_cost_lb else self.cost_pm)

    def margin(self, use_cost_lb=True):
    gain = self.gain(use_cost_lb)
    return (gain / self.price) * 100 if self.price > 0 else 0

    def to_dict(self):
    return {
    "name": self.name,
    "price": self.price,
    "cost_lb": self.cost_lb,
    "cost_pm": self.cost_pm,
    "weight": self.weight,
    "store_prices": self.store_prices
    }

    @classmethod
    def from_dict(cls, data):
    return cls(data['name'], data['price'], data['cost_lb'], data['cost_pm'], data['weight'], data.get('store_prices', {}))

    class Store:
    def __init__(self):
    self.products = []
    self.load_products()

    def add_product(self, name, price, cost_lb, cost_pm, weight):
    product = Product(name, price, cost_lb, cost_pm, weight)
    self.products.append(product)
    self.save_products()

    def delete_product(self, name):
    self.products = [product for product in self.products if product.name != name]
    self.save_products()

    def update_product(self, name, price=None, cost_lb=None, cost_pm=None, weight=None):
    for product in self.products:
    if product.name == name:
    if price is not None:
    product.price = price
    if cost_lb is not None:
    product.cost_lb = cost_lb
    if cost_pm is not None:
    product.cost_pm = cost_pm
    if weight is not None:
    product.weight = weight
    break
    self.save_products()

    def save_products(self):
    with open('products.json', 'w') as f:
    json.dump([product.to_dict() for product in self.products], f)

    def load_products(self):
    if os.path.exists('products.json'):
    with open('products.json', 'r') as f:
    self.products = [Product.from_dict(item) for item in json.load(f)]

    class StoreApp:
    def __init__(self, root):
    self.root = root
    self.root.title("RenoCart")
    self.store = Store()
    self.users = User.load_users() # Load existing users
    self.current_user = None # Track the currently logged-in user

    self.show_menu()

    def center_window(self, window, width, height):
    screen_width = window.winfo_screenwidth()
    screen_height = window.winfo_screenheight()

    x = (screen_width // 2) - (width // 2)
    y = (screen_height // 2) - (height // 2)

    window.geometry(f"{width}x{height}+{x}+{y}")

    def show_menu(self):
    self.clear_frame()
    menu_window = tk.Toplevel(self.root)
    menu_window.title("Main Menu")
    self.center_window(menu_window, 210, 250)


    title_label = tk.Label(menu_window, text="RenoCart", font=("Helvetica", 16))
    title_label.pack(pady=10)

    # Login button
    login_button = tk.Button(menu_window, text="Login", command=self.open_login_window) # Changed this line
    login_button.pack(pady=5)

    # Sign-up button
    signup_button = tk.Button(menu_window, text="Sign Up", command=self.signup_user)
    signup_button.pack(padx=7.5)

    # Quit Button
    quit_button = tk.Button(menu_window, text="Close", command=self.root.quit)
    quit_button.pack(pady=10)

    def signup_user(self):
    signup_window = tk.Toplevel(self.root)
    signup_window.title("Sign up")
    signup_window.geometry("300x200")
    self.center_window(signup_window, 210, 220)

    # Username label and entry
    username_label = tk.Label(signup_window, text="Username:")
    username_label.pack()
    username_entry = tk.Entry(signup_window)
    username_entry.pack()

    # Password label and entry
    password_label = tk.Label(signup_window, text="Password:")
    password_label.pack()
    password_entry = tk.Entry(signup_window, show="*")
    password_entry.pack()

    # Code for signup
    code_label = tk.Label(signup_window, text="Sign Up Code:")
    code_label.pack()
    code_entry = tk.Entry(signup_window)
    code_entry.pack(pady=5)

    # Sign Up button
    signup_button = tk.Button(signup_window, text="Sign Up", command=lambda: self.signup(username_entry.get(), password_entry.get(), code_entry.get()))
    signup_button.pack(pady=10)

    # Back to menu button
    back_button = tk.Button(signup_window, text="Back", command=self.show_menu)
    back_button.pack()

    def signup (self, username, password, code):
    required_code = "1234" # The required code for sign-up
    if code != required_code:
    tk.messagebox.showerror("Error", "Invalid sign-up code.")
    return

    if any(user.username == username for user in self.users):
    tk.messagebox.showerror("Error", "Username already exists.")
    return

    new_user = User(username, password)
    self.users.append(new_user)
    User.save_users(self.users)

    tk.messagebox.showinfo("Success", "User signed up successfully!")

    def clear_frame(self):
    # Remove all widgets from the frame
    for widget in self.root.winfo_children():
    widget.destroy()

    def open_login_window(self):
    login_window = tk.Toplevel(self.root)
    login_window.title("Login")
    login_window.geometry("300x200")
    self.center_window(login_window, 210, 220)

    # Username Entry
    username_label = tk.Label(login_window, text="Username:")
    username_label.pack(pady=5)
    username_entry = tk.Entry(login_window)
    username_entry.pack(pady=5)

    # Password Entry
    password_label = tk.Label(login_window, text="Password:")
    password_label.pack(pady=5)
    password_entry = tk.Entry(login_window, show="*")
    password_entry.pack(pady=5)

    # Login Button
    login_button = tk.Button(login_window, text="Login", command=lambda: self.login_user(username_entry.get(), password_entry.get(), login_window))
    login_button.pack(pady=10)

    # Cancel Button
    cancel_button = tk.Button(login_window, text="Cancel", command=login_window.destroy)
    cancel_button.pack(pady=5)

    def login_user(self, username, password, login_window):
    users = User.load_users() # Load users from the JSON file
    for user in users:
    if user.username == username and user.password == password:
    tk.messagebox.showinfo("Success", "Login successful!")
    login_window.destroy() # Close the login window
    self.access_app() # Proceed to the next screen
    return
    tk.messagebox.showerror("Error", "Invalid username or password.")

    def access_app(self):
    self.clear_frame()
    self.create_widgets()

    def create_widgets(self):
    # Input
    self.name_var = tk.StringVar()
    self.price_var = tk.DoubleVar()
    self.cost_lb_var = tk.DoubleVar()
    self.cost_pm_var = tk.DoubleVar()
    self.weight_var = tk.DoubleVar()
    self.cost_type = tk.StringVar(value="L&B")
    self.search_var = tk.StringVar()
    self.weight_unit = tk.StringVar(value="lbs")

    tk.Label(self.root, text="Name:").grid(row=0, column=0)
    tk.Entry(self.root, textvariable=self.name_var).grid(row=0, column=1)
    tk.Button(self.root, text="Add", command=self.add_product).grid(row=0, column=2)

    tk.Label(self.root, text="Price:").grid(row=1, column=0)
    tk.Entry(self.root, textvariable=self.price_var).grid(row=1, column=1)
    tk.Button(self.root, text="Delete", command=self.delete_product).grid(row=1, column=2)

    tk.Label(self.root, text="L&B Cost:").grid(row=2, column=0)
    tk.Entry(self.root, textvariable=self.cost_lb_var).grid(row=2, column=1)
    tk.Button(self.root, text="Update", command=self.update_product).grid(row=2, column=2)

    tk.Label(self.root, text="Pont-Masson Cost:").grid(row=3, column=0)
    tk.Entry(self.root, textvariable=self.cost_pm_var).grid(row=3, column=1)

    tk.Label(self.root, text="Weight (lbs):").grid(row=4, column=0)
    tk.Entry(self.root, textvariable=self.weight_var).grid(row=4, column=1)

    # Weight unit selection
    tk.Label(self.root, text="Weight Unit:").grid(row=4, column=2)
    tk.OptionMenu(self.root, self.weight_unit, "lbs", "kg").grid(row=4, column=3)

    # Cost type selection
    tk.Label(self.root, text="Select Cost Type for Margin:").grid(row=5, column=0, columnspan=2)
    tk.OptionMenu(self.root, self.cost_type, "L&B", "Pont-Masson").grid(row=6, column=0, columnspan=2)

    # Search
    tk.Label(self.root, text="Search:").grid(row=7, column=0)
    tk.Entry(self.root, textvariable=self.search_var).grid(row=7, column=1)
    tk.Button(self.root, text="Search", command=self.search_products).grid(row=7, column=2)

    # Refresh button
    tk.Button(self.root, text="Refresh", command=self.show_products).grid(row=8, column=0, columnspan=3, sticky="w")

    # Clear button
    tk.Button(self.root, text="Clear", command=self.clear_selection).grid(row=3, column=2)

    # Compare Prices button
    tk.Button(self.root, text="Compare Prices", command=self.open_price_comparison).grid(row=8, column=3)

    # Treeview for displaying products
    self.tree = ttk.Treeview(self.root, columns=("Name", "Cost", "Price", "Gain", "Margin", "Weight"), show='headings')
    self.tree.heading("Name", text="Name")
    self.tree.heading("Cost", text="Cost")
    self.tree.heading("Price", text="Price")
    self.tree.heading("Gain", text="Gain")
    self.tree.heading("Margin", text="Margin (%)")
    self.tree.heading("Weight", text="Weight")

    # Column centering
    for col in ("Cost", "Price", "Gain", "Margin", "Weight"):
    self.tree.column(col, anchor='center')

    self.tree.grid(row=9, column=0, columnspan=5)

    self.tree.bind("<<TreeviewSelect>>", self.on_product_select) # Bind selection event

    # Show products
    self.show_products()

    def add_product(self):
    name = self.name_var.get()
    try:
    price = self.price_var.get()
    cost_lb = self.cost_lb_var.get()
    cost_pm = self.cost_pm_var.get()
    weight = self.weight_var.get()

    if not name:
    raise ValueError("Product name cannot be empty.")
    if price < 0 or cost_lb < 0 or cost_pm < 0 or weight < 0:
    raise ValueError("Price and costs must be non-negative.")

    self.store.add_product(name, price, cost_lb, cost_pm, weight)
    self.clear_entries()
    self.show_products()
    except ValueError as e:
    messagebox.showerror("Error", str(e))

    def delete_product(self):
    selected_item = self.tree.selection()
    if not selected_item:
    messagebox.showwarning("Select Product", "Please select a product to delete.")
    return
    item_id = selected_item[0]
    product_name = self.tree.item(item_id, "values")[0]

    confirm_delete = messagebox.askyesno("Confirm Delete", f"Are you sure you want to delete '{product_name}'?")
    if confirm_delete:
    self.store.delete_product(product_name)
    messagebox.showinfo("Success", f"Product '{product_name}' deleted.")
    self.clear_entries()
    self.show_products()

    def update_product(self):
    selected_item = self.tree.selection()
    if not selected_item:
    messagebox.showwarning("Select Product", "Please select a product to update.")
    return
    item_id = selected_item[0]
    product_name = self.tree.item(item_id, "values")[0]

    price = self.price_var.get() or None
    cost_lb = self.cost_lb_var.get() or None
    cost_pm = self.cost_pm_var.get() or None
    weight = self.weight_var.get() or None

    self.store.update_product(product_name, price, cost_lb, cost_pm, weight)
    messagebox.showinfo("Success", f"Product '{product_name}' updated.")
    self.clear_entries()
    self.show_products()

    def show_products(self):

    for item in self.tree.get_children():
    self.tree.delete(item)
    for product in self.store.products:
    use_cost_lb = self.cost_type.get() == "L&B"
    cost = product.cost_lb if use_cost_lb else product.cost_pm
    gain = product.gain(use_cost_lb)
    margin = product.margin(use_cost_lb)

    # Convert weight based on selection
    weight_display = product.weight
    if self.weight_unit.get() == "kg":
    weight_display = weight_display * 0.453592 # Convert lbs to kg

    self.tree.insert("", "end", values=(
    product.name,
    f"${cost:.2f}",
    f"${product.price:.2f}",
    f"${gain:.2f}",
    f"{margin:.2f}%",
    f"{weight_display:.2f} {self.weight_unit.get()}"
    ))

    def search_products(self):
    search_term = self.search_var.get().lower()
    for item in self.tree.get_children():
    self.tree.delete(item)
    for product in self.store.products:
    if search_term in product.name.lower():
    use_cost_lb = self.cost_type.get() == "L&B"
    cost = product.cost_lb if use_cost_lb else product.cost_pm
    gain = product.gain(use_cost_lb)
    margin = product.margin(use_cost_lb)

    weight_display = product.weight
    if self.weight_unit.get() == "kg":
    weight_display = weight_display * 0.453592

    self.tree.insert("", "end", values=(
    product.name,
    f"${cost:.2f}",
    f"${product.price:.2f}",
    f"${gain:.2f}",
    f"{margin:.2f}%",
    f"{weight_display:.2f} {self.weight_unit.get()}"
    ))

    def clear_entries(self):
    self.name_var.set("")
    self.price_var.set(0)
    self.cost_lb_var.set(0)
    self.cost_pm_var.set(0)
    self.weight_var.set(0)

    def clear_selection(self):
    self.tree.selection_remove(self.tree.selection()) # Deselect any selected item
    self.clear_entries()

    def on_product_select(self, event):
    selected_item = self.tree.selection()
    if not selected_item:
    return

    item_id = selected_item[0]
    product_data = self.tree.item(item_id, "values")

    # Order of values in product_data is (Name, Cost, Price, Gain, Margin, Weight)
    self.name_var.set(product_data[0]) # Set product name
    self.cost_lb_var.set(float(product_data[1][1:])) # L&B Cost (remove '$' sign)
    self.price_var.set(float(product_data[2][1:])) # Price (remove '$' sign)
    self.cost_pm_var.set(float(product_data[1][1:])) # Pont-Masson Cost (remove '$' sign)
    self.weight_var.set(float(product_data[5].split()[0])) # Weight (extract value)

    def open_price_comparison(self):
    selected_item = self.tree.selection()
    if not selected_item:
    messagebox.showwarning("Select Product", "Please select a product to compare prices.")
    return
    item_id = selected_item[0]
    product_name = self.tree.item(item_id, "values")[0]
    product = next((p for p in self.store.products if p.name == product_name), None)
    if product:
    PriceComparisonWindow(self.root, product)

    class PriceComparisonWindow:
    def __init__(self, parent, product):
    self.top = tk.Toplevel(parent)
    self.top.title(f"Price Comparison for {product.name}")
    self.product = product

    # Store labels and entry fields for each store
    self.store_vars = {
    "Rona": tk.DoubleVar(value=self.product.store_prices.get("Rona", 0)),
    "Home Depot": tk.DoubleVar(value=self.product.store_prices.get("Home Depot", 0)),
    "Patrick Morin": tk.DoubleVar(value=self.product.store_prices.get("Patrick Morin", 0)),
    "BMR": tk.DoubleVar(value=self.product.store_prices.get("BMR", 0)),
    }

    tk.Label(self.top, text="Store Prices").grid(row=0, column=0, columnspan=2)

    for idx, (store, var) in enumerate(self.store_vars.items()):
    tk.Label(self.top, text=store).grid(row=idx + 1, column=0)
    entry = tk.Entry(self.top, textvariable=var)
    entry.grid(row=idx + 1, column=1)
    entry.bind("<KeyRelease>", self.update_price_info) # Update on key release

    tk.Button(self.top, text="Save", command=self.save_prices).grid(row=len(self.store_vars) + 1, columnspan=2)

    # Labels for comparison results
    self.comparison_labels = {}
    for idx, store in enumerate(self.store_vars.keys()):
    label = tk.Label(self.top, text="")
    label.grid(row=len(self.store_vars) + 2 + idx, columnspan=2)
    self.comparison_labels[store] = label

    # Label for cheapest store
    self.cheapest_store_label = tk.Label(self.top, text="", font=('Arial', 10, 'bold'))
    self.cheapest_store_label.grid(row=len(self.store_vars) + len(self.comparison_labels) + 2, columnspan=2)

    # Label for most expensive store
    self.most_expensive_store_label = tk.Label(self.top, text="", font=('Arial', 10, 'bold'))
    self.most_expensive_store_label.grid(row=len(self.store_vars) + len(self.comparison_labels) + 3, columnspan=2)

    self.update_price_info()

    def save_prices(self):
    for store, var in self.store_vars.items():
    self.product.store_prices[store] = var.get()
    messagebox.showinfo("Success", "Prices saved successfully.")
    self.update_price_info()

    def update_price_info(self, event=None):
    prices = {store: var.get() for store, var in self.store_vars.items()}

    # Update comparison labels
    for store, price in prices.items():
    if self.product.price < price:
    comparison_text = f"Your price is Lower than {store}"
    elif self.product.price > price:
    comparison_text = f"Your price is Higher than {store}"
    else:
    comparison_text = f"Your price is the Same as {store}"
    self.comparison_labels[store].config(text=comparison_text)

    # Calculate the cheapest store
    cheapest_store = min(prices, key=prices.get)
    cheapest_price = prices[cheapest_store]
    self.cheapest_store_label.config(text=f"Cheapest Store: {cheapest_store} - ${cheapest_price:.2f}")

    # Calculate the most expensive store
    most_expensive_store = max(prices, key=prices.get)
    most_expensive_price = prices[most_expensive_store]
    self.most_expensive_store_label.config(text=f"Most Expensive Store: {most_expensive_store} - ${most_expensive_price:.2f}")

    if __name__ == "__main__":
    root = tk.Tk()
    app = StoreApp(root)
    root.mainloop()

    Continue reading...

Compartilhe esta Página