"""A window with two buttons, one per direction. python gui.py Pick a direction, pick a file; the log shows what was chosen before the conversion runs and where the result landed afterwards. Output goes next to the input under a timestamped name, so nothing is ever overwritten. """ import os, sys, traceback import tkinter as tk from tkinter import ttk, messagebox import config, sab, sax class App: def __init__(self, root): self.root = root root.title("%s %s" % (config.NAME, config.VERSION)) root.minsize(620, 340) frame = ttk.Frame(root, padding=12) frame.pack(fill="both", expand=True) frame.columnconfigure(0, weight=1) frame.rowconfigure(3, weight=1) # sedona home home_box = ttk.LabelFrame(frame, text="Sedona home", padding=(8, 4)) home_box.grid(row=0, column=0, sticky="ew") home_box.columnconfigure(0, weight=1) self.home_var = tk.StringVar(value="(not set)") ttk.Label(home_box, textvariable=self.home_var).grid(row=0, column=0, sticky="w") ttk.Button(home_box, text="Change...", command=self.change_home).grid(row=0, column=1, padx=(8, 0)) # the two directions buttons = ttk.Frame(frame, padding=(0, 12)) buttons.grid(row=1, column=0, sticky="ew") buttons.columnconfigure(0, weight=1) buttons.columnconfigure(1, weight=1) self.b_sab2sax = ttk.Button(buttons, text="SAB file to SAX file", padding=10, command=lambda: self.run("sab")) self.b_sab2sax.grid(row=0, column=0, sticky="ew", padx=(0, 6)) self.b_sax2sab = ttk.Button(buttons, text="SAX file to SAB file", padding=10, command=lambda: self.run("sax")) self.b_sax2sab.grid(row=0, column=1, sticky="ew", padx=(6, 0)) self.status = tk.StringVar(value="Choose a direction.") ttk.Label(frame, textvariable=self.status).grid(row=2, column=0, sticky="w") # log log_box = ttk.Frame(frame) log_box.grid(row=3, column=0, sticky="nsew", pady=(8, 0)) log_box.columnconfigure(0, weight=1) log_box.rowconfigure(0, weight=1) self.log_text = tk.Text(log_box, height=10, wrap="word", state="disabled") self.log_text.grid(row=0, column=0, sticky="nsew") bar = ttk.Scrollbar(log_box, orient="vertical", command=self.log_text.yview) bar.grid(row=0, column=1, sticky="ns") self.log_text.configure(yscrollcommand=bar.set) self.home = None root.after(50, self.first_home) # ---------- helpers ---------- def log(self, line=""): self.log_text.configure(state="normal") self.log_text.insert("end", line + "\n") self.log_text.see("end") self.log_text.configure(state="disabled") self.root.update_idletasks() # so it is on screen before the work starts def set_home(self, path): self.home = path self.home_var.set(path or "(not set)") def first_home(self): """Resolve sedona home at startup; the folder chooser opens if it isn't set.""" try: self.set_home(config.get_sedona_home()) self.log("sedona home: %s" % self.home) except SystemExit as e: self.log("no sedona home: %s" % e) messagebox.showwarning(config.title("Sedona home"), "No sedona home selected.\n\n" "Set it with Change... before converting.") def change_home(self): picked = config.ask_directory("Select Sedona home (the folder containing manifests/)", self.home) if not picked: return picked = os.path.normpath(picked) if not config.is_sedona_home(picked): messagebox.showerror(config.title("Sedona home"), "%s has no manifests/ subfolder." % picked) return config.write_prop(config.KEY, picked) self.set_home(picked) self.log("sedona home: %s (saved to %s)" % (picked, config.PROPS)) # ---------- conversion ---------- def run(self, direction): """direction: 'sab' means sab -> sax, 'sax' means sax -> sab.""" if not config.is_sedona_home(self.home): messagebox.showerror(config.title("Sedona home"), "Set a valid sedona home first (Change...).") return src = config.choose_sab_input() if direction == "sab" else config.choose_sax_file() if not src: self.status.set("Cancelled.") return src = os.path.abspath(src) # show what was chosen, before doing anything with it self.log() self.log("%s -> %s" % (direction, "sax" if direction == "sab" else "sab")) self.log(" INPUT FILE:") self.log(" %s" % os.path.basename(src)) self.log(" INPUT PATH:") self.log(" %s" % os.path.dirname(src)) self.status.set("Converting %s ..." % os.path.basename(src)) if direction == "sab": out = os.path.join(os.path.dirname(src), sax.default_sax_name(src)) work = lambda: sax.decode(src, self.home, out) else: out = os.path.join(os.path.dirname(src), sab.default_sab_name(src)) work = lambda: sab.encode(src, self.home, out) self.busy(True) try: n = work() except Exception as e: self.busy(False) self.log(" FAILED: %s" % e) self.status.set("Failed.") traceback.print_exc() messagebox.showerror(config.title("Conversion failed"), str(e)) return self.busy(False) self.log(" OUTPUT FILE:") self.log(" %s" % os.path.basename(out)) self.log(" OUTPUT PATH:") self.log(" %s" % os.path.dirname(out)) self.log(" wrote %d bytes" % n) self.status.set("Done: %s" % os.path.basename(out)) def busy(self, on): state = "disabled" if on else "normal" self.b_sab2sax.configure(state=state) self.b_sax2sab.configure(state=state) self.root.configure(cursor="watch" if on else "") self.root.update_idletasks() def main(): root = tk.Tk() App(root) root.mainloop() return 0 if __name__ == "__main__": sys.exit(main())