import os import threading import queue import webbrowser from pathlib import Path import tkinter as tk from tkinter import ttk, scrolledtext, messagebox, filedialog from bot import BotRunner from config import DEFAULT_CREDENTIALS_FILE, save_credentials LOG_POLL_MS = 200 class GUI: def __init__(self, root: tk.Tk): self.root = root root.title("ADM PURCHASING TOOLS") root.geometry("760x620") self.log_queue = queue.Queue() self.stop_event = threading.Event() self.worker_thread = None frm = ttk.Frame(root, padding=12) frm.pack(fill=tk.BOTH, expand=True) # Inputs inputs = ttk.Frame(frm) inputs.pack(fill=tk.X) ttk.Label(inputs, text="Username:").grid(row=0, column=0, sticky=tk.W, pady=4) self.username_var = tk.StringVar() ttk.Entry(inputs, textvariable=self.username_var, width=40).grid(row=0, column=1, sticky=tk.W) ttk.Label(inputs, text="Password:").grid(row=1, column=0, sticky=tk.W, pady=4) self.password_var = tk.StringVar() ttk.Entry(inputs, textvariable=self.password_var, width=40, show="*").grid(row=1, column=1, sticky=tk.W) ttk.Label(inputs, text="Studio URL or Name:").grid(row=2, column=0, sticky=tk.W, pady=4) self.studio_var = tk.StringVar() ttk.Entry(inputs, textvariable=self.studio_var, width=60).grid(row=2, column=1, sticky=tk.W) ttk.Label(inputs, text="Minimum Price:").grid(row=3, column=0, sticky=tk.W, pady=4) self.min_price_var = tk.StringVar(value="6.00") ttk.Entry(inputs, textvariable=self.min_price_var, width=12).grid(row=3, column=1, sticky=tk.W) # Buttons btns = ttk.Frame(frm, padding=(0, 8, 0, 8)) btns.pack(fill=tk.X) self.start_btn = ttk.Button(btns, text="Start", command=self.start) self.start_btn.pack(side=tk.LEFT, padx=6) self.stop_btn = ttk.Button(btns, text="Stop", command=self.stop, state=tk.DISABLED) self.stop_btn.pack(side=tk.LEFT, padx=6) ttk.Button(btns, text="Open Export Folder", command=self.open_exports).pack(side=tk.LEFT, padx=6) ttk.Button(btns, text="Save Credentials", command=self.save_creds).pack(side=tk.LEFT, padx=6) # Status / Progress status_frame = ttk.Frame(frm) status_frame.pack(fill=tk.X) self.status_var = tk.StringVar(value="Ready") ttk.Label(status_frame, textvariable=self.status_var).pack(side=tk.LEFT) self.progress = ttk.Progressbar(status_frame, mode="indeterminate") self.progress.pack(fill=tk.X, padx=8, pady=6) # Log panel ttk.Label(frm, text="Logs:").pack(anchor=tk.W) self.log_widget = scrolledtext.ScrolledText(frm, height=20, state=tk.DISABLED) self.log_widget.pack(fill=tk.BOTH, expand=True) # Poll logs root.after(LOG_POLL_MS, self._poll_log) def append_log(self, text: str): self.log_widget.configure(state=tk.NORMAL) self.log_widget.insert(tk.END, text + "\n") self.log_widget.see(tk.END) self.log_widget.configure(state=tk.DISABLED) def _poll_log(self): try: while True: item = self.log_queue.get_nowait() if isinstance(item, dict) and item.get("type") == "status": self.status_var.set(item.get("text", "")) else: self.append_log(str(item)) except queue.Empty: pass finally: self.root.after(LOG_POLL_MS, self._poll_log) def start(self): username = self.username_var.get().strip() password = self.password_var.get().strip() studio = self.studio_var.get().strip() try: min_price = float(self.min_price_var.get().strip()) except Exception: messagebox.showerror("Invalid input", "Minimum price must be a number") return if not username or not password or not studio: messagebox.showerror("Missing fields", "Please enter username, password and studio URL/name") return self.stop_event.clear() self.start_btn.config(state=tk.DISABLED) self.stop_btn.config(state=tk.NORMAL) self.progress.start(10) self.status_var.set("Starting...") runner = BotRunner( username=username, password=password, studio_input=studio, min_price=min_price, log_queue=self.log_queue, stop_event=self.stop_event, ) def worker(): try: runner.run() self.log_queue.put({"type": "status", "text": "Completed"}) except Exception as e: self.log_queue.put(f"Error: {e}") finally: self.progress.stop() self.start_btn.config(state=tk.NORMAL) self.stop_btn.config(state=tk.DISABLED) self.worker_thread = threading.Thread(target=worker, daemon=True) self.worker_thread.start() def stop(self): if messagebox.askyesno("Stop", "Stop the running job?"): self.stop_event.set() self.status_var.set("Stopping...") def open_exports(self): p = Path.cwd() webbrowser.open(p.as_uri()) def save_creds(self): u = self.username_var.get().strip() p = self.password_var.get().strip() if not u or not p: messagebox.showerror("Error", "Provide both username and password to save") return save_credentials(u, p, DEFAULT_CREDENTIALS_FILE) messagebox.showinfo("Saved", f"Credentials saved to {DEFAULT_CREDENTIALS_FILE}") def run_gui(): root = tk.Tk() gui = GUI(root) root.mainloop()