#!/usr/bin/env python3 """Graphical front-end for process_exp.py. Pick a SonicWall .exp file, click Run, and get a styled HTML report. Usage: python3 process_exp_gui.py [file.exp] """ import queue import subprocess import sys import threading import tkinter as tk import webbrowser from pathlib import Path from tkinter import filedialog, ttk sys.path.insert(0, str(Path(__file__).resolve().parent)) import process_exp class App(tk.Tk): def __init__(self): super().__init__() self.title("SonicWall .exp to HTML converter") self.geometry("600x360") self.minsize(480, 300) self.exp_path = None self.busy = False self.q = queue.Queue() pad = {"padx": 10, "pady": 6} top = ttk.Frame(self) top.pack(fill="x", **pad) ttk.Label(top, text="EXP file:").pack(side="left") self.file_var = tk.StringVar(value="(none selected)") self.file_label = ttk.Label( top, textvariable=self.file_var, foreground="#666", anchor="w") self.file_label.pack(side="left", fill="x", expand=True, padx=(8, 8)) ttk.Button(top, text="Browse...", command=self.choose_file).pack(side="right") self.run_btn = ttk.Button(self, text="Run conversion", command=self.run, state="disabled") self.run_btn.pack(fill="x", **pad) self.status_var = tk.StringVar(value="Ready. Pick a .exp file and press Run.") ttk.Label(self, textvariable=self.status_var, anchor="w", foreground="#333").pack(fill="x", padx=12) self.open_html_btn = ttk.Button(self, text="Open HTML in browser", command=self.open_html, state="disabled") self.open_html_btn.pack(anchor="e", padx=12, pady=(0, 8)) self.log = tk.Text(self, height=10, state="disabled", wrap="word", background="#f7f8fa") self.log.pack(fill="both", expand=True, padx=10, pady=(0, 10)) self.after(100, self.poll_queue) # ------------------------------------------------------------ file pick def choose_file(self): if self.busy: return path = filedialog.askopenfilename( title="Choose a SonicWall .exp file", filetypes=[("EXP files", "*.exp"), ("All files", "*.*")], ) if path: self.set_file(Path(path)) def set_file(self, path: Path): if not path.is_file(): self.append_log("error: no such file: %s" % path) return self.exp_path = path self.file_var.set(str(path)) self.run_btn.configure(state="normal") self.open_html_btn.configure(state="disabled") self.status_var.set("Ready to convert %s" % path.name) # ------------------------------------------------------------ conversion def run(self): if self.busy or self.exp_path is None: return self.busy = True self.run_btn.configure(state="disabled") self.status_var.set("Converting %s ..." % self.exp_path.name) threading.Thread(target=self._worker, daemon=True).start() def _worker(self): try: html_path = process_exp.convert(self.exp_path) self.q.put(("ok", html_path, None)) except Exception as exc: self.q.put(("err", None, exc)) def poll_queue(self): try: while True: kind, html_path, exc = self.q.get_nowait() if kind == "ok": self.open_html_btn.configure(state="normal") self.open_html_btn.configure( command=lambda p=html_path: webbrowser.open(str(p))) self.status_var.set("Done: %s" % html_path) self.append_log("wrote %s" % html_path) else: self.status_var.set("Failed: %s" % exc) self.append_log("error: %s" % exc) self.busy = False self.run_btn.configure(state="normal") except queue.Empty: pass self.after(100, self.poll_queue) # -------------------------------------------------------------- helpers def open_html(self): if self.exp_path is not None: webbrowser.open(str(self.exp_path.with_suffix(".html"))) def append_log(self, text: str): self.log.configure(state="normal") self.log.insert("end", text + "\n") self.log.see("end") self.log.configure(state="disabled") def main() -> int: app = App() if len(sys.argv) > 1: app.after(100, lambda: app.set_file(Path(sys.argv[1]))) app.mainloop() return 0 if __name__ == "__main__": sys.exit(main())