#!/usr/bin/env python3 """License generator GUI for regInfo.""" import subprocess import json import os import sys import tempfile from datetime import date from tkinter import Tk, Frame, Label, Entry, Button, StringVar, messagebox, filedialog from tkinter import font as tkfont from tkinter.ttk import Combobox, Style # Resolve the regInfo binary: try same dir, then parent dir (dev layout: gui/ -> build/) def _find_binary(): if getattr(sys, 'frozen', False): # PyInstaller --onefile extracts bundled binaries to _MEIPASS base = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(sys.executable))) else: base = os.path.dirname(os.path.abspath(__file__)) candidates = [ os.path.join(base, "regInfo"), os.path.join(base, "regInfo.exe"), os.path.join(base, "..", "regInfo"), os.path.join(base, "..", "build", "Release", "regInfo.exe"), os.path.join(base, "..", "build", "regInfo"), ] for p in candidates: if os.path.isfile(p) and os.access(p, os.X_OK): return p return candidates[0] # default, will fail with clear error BINARY = _find_binary() def get_machine_code(): try: result = subprocess.run([BINARY, "machinecode"], capture_output=True, text=True, check=True) return result.stdout.strip() except Exception as e: messagebox.showerror("错误", f"获取机器码失败:\n{e}\n\n请确认 {BINARY} 存在且可执行") return "" class LicenseGUI: def __init__(self): self.root = Tk() self.root.title("注册文件生成器") self.root.resizable(False, False) # Font: 宋体 for Chinese, with platform fallbacks families = ["宋体", "SimSun", "Noto Serif CJK SC", "AR PL UMing CN", "serif"] available = [f for f in families if f in tkfont.families()] self._font = tkfont.Font(family=available[0] if available else "TkDefaultFont", size=10) self.root.option_add("*Font", self._font) # -- machine code -- machine_code = get_machine_code() if not machine_code: self.root.destroy() return pad = {"padx": 10, "pady": 6} # Row 0: machine code Label(self.root, text="机器码:").grid(row=0, column=0, sticky="e", **pad) self.mc_var = StringVar(value=machine_code) Entry(self.root, textvariable=self.mc_var, width=68).grid(row=0, column=1, columnspan=2, sticky="w", **pad) # Row 1: name Label(self.root, text="名称:").grid(row=1, column=0, sticky="e", **pad) self.name_var = StringVar() Entry(self.root, textvariable=self.name_var, width=35).grid(row=1, column=1, sticky="w", **pad) # Row 2: phone Label(self.root, text="电话:").grid(row=2, column=0, sticky="e", **pad) self.phone_var = StringVar() Entry(self.root, textvariable=self.phone_var, width=35).grid(row=2, column=1, sticky="w", **pad) # Row 3: unit Label(self.root, text="单位:").grid(row=3, column=0, sticky="e", **pad) self.unit_var = StringVar() Entry(self.root, textvariable=self.unit_var, width=35).grid(row=3, column=1, sticky="w", **pad) # Row 4: auth datetime (auto today) today_str = date.today().strftime("%Y-%m-%d") Label(self.root, text="授权日期:").grid(row=4, column=0, sticky="e", **pad) self.auth_var = StringVar(value=today_str) auth_entry = Entry(self.root, textvariable=self.auth_var, width=20, state="readonly", readonlybackground="#f0f0f0") auth_entry.grid(row=4, column=1, sticky="w", **pad) # Row 5: over datetime (date picker) Label(self.root, text="到期日期:").grid(row=5, column=0, sticky="e", **pad) over_frame = Frame(self.root) over_frame.grid(row=5, column=1, columnspan=2, sticky="w", **pad) self.over_year = StringVar(value=str(date.today().year + 1)) self.over_month = StringVar(value="01") self.over_day = StringVar(value="01") self._build_date_picker(over_frame, self.over_year, self.over_month, self.over_day) # Row 6: update datetime (date picker) Label(self.root, text="升级到期:").grid(row=6, column=0, sticky="e", **pad) update_frame = Frame(self.root) update_frame.grid(row=6, column=1, columnspan=2, sticky="w", **pad) self.update_year = StringVar(value=str(date.today().year + 1)) self.update_month = StringVar(value="01") self.update_day = StringVar(value="01") self._build_date_picker(update_frame, self.update_year, self.update_month, self.update_day) # Row 7: save path Label(self.root, text="输出目录:").grid(row=7, column=0, sticky="e", **pad) self.path_var = StringVar(value=os.getcwd()) Entry(self.root, textvariable=self.path_var, width=50).grid(row=7, column=1, sticky="w", **pad) Button(self.root, text="选择...", command=self._choose_dir).grid(row=7, column=2, sticky="w", **pad) # Row 8: filename preview Label(self.root, text="文件名:").grid(row=8, column=0, sticky="e", **pad) self.filename_var = StringVar() self.filename_label = Entry(self.root, textvariable=self.filename_var, width=60, state="readonly", readonlybackground="#f0f0f0") self.filename_label.grid(row=8, column=1, columnspan=2, sticky="w", **pad) self._update_filename() self.name_var.trace_add("write", lambda *a: self._update_filename()) # Row 9: buttons btn_frame = Frame(self.root) btn_frame.grid(row=9, column=0, columnspan=3, pady=15) self.gen_btn = Button(btn_frame, text="生成激活", font=("", 14, "bold"), width=15, height=2, command=self._generate) self.gen_btn.pack(side="left", padx=5) self.open_btn = Button(btn_frame, text="打开授权文件", font=("", 14), width=15, height=2, command=self._open_license) self.open_btn.pack(side="left", padx=5) self.root.bind("", lambda e: self._generate()) self.root.mainloop() def _build_date_picker(self, parent, year_var, month_var, day_var): years = [str(y) for y in range(2020, 2101)] months = [f"{m:02d}" for m in range(1, 13)] days = [f"{d:02d}" for d in range(1, 32)] cy = Combobox(parent, textvariable=year_var, values=years, width=6, state="readonly") cy.pack(side="left") Label(parent, text="年").pack(side="left") cm = Combobox(parent, textvariable=month_var, values=months, width=4, state="readonly") cm.pack(side="left") Label(parent, text="月").pack(side="left") cd = Combobox(parent, textvariable=day_var, values=days, width=4, state="readonly") cd.pack(side="left") Label(parent, text="日").pack(side="left") def _update_filename(self): name = self.name_var.get().strip() or "unknown" mc_short = (self.mc_var.get() or "00000000")[:8] self.filename_var.set(f"license_{name}_{mc_short}.xlts") def _choose_dir(self): d = filedialog.askdirectory(initialdir=self.path_var.get()) if d: self.path_var.set(d) def _generate(self): name = self.name_var.get().strip() if not name: messagebox.showwarning("提示", "请输入名称") return phone = self.phone_var.get().strip() if not phone.isdigit() or len(phone) != 11: messagebox.showwarning("提示", "请输入正确的11位手机号") return license_data = { "name": name, "machinecode": self.mc_var.get(), "phone": phone, "unit": self.unit_var.get().strip(), "authdatetime": self.auth_var.get(), "overdatetime": f"{self.over_year.get()}-{self.over_month.get()}-{self.over_day.get()}", "updatedatetime": f"{self.update_year.get()}-{self.update_month.get()}-{self.update_day.get()}", } with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False, encoding="utf-8") as f: json.dump(license_data, f, ensure_ascii=False) json_path = f.name outfile = os.path.join(self.path_var.get(), self.filename_var.get()) try: result = subprocess.run( [BINARY, "generate", "-f", json_path, outfile], capture_output=True, text=True, check=True) messagebox.showinfo("成功", f"授权文件已生成:\n{outfile}") except subprocess.CalledProcessError as e: messagebox.showerror("失败", f"生成失败:\n{e.stderr}") finally: os.unlink(json_path) def _parse_date(self, date_str, year_var, month_var, day_var): """Parse 'YYYY-MM-DD' into year/month/day vars. Ignores on failure.""" parts = date_str.strip().split("-") if len(parts) == 3: year_var.set(parts[0]) month_var.set(parts[1]) day_var.set(parts[2]) def _open_license(self): f = filedialog.askopenfilename( title="选择授权文件", filetypes=[("授权文件", "*.xlts"), ("所有文件", "*.*")]) if not f: return try: result = subprocess.run([BINARY, "read", f], capture_output=True, text=True, check=True) data = json.loads(result.stdout) except subprocess.CalledProcessError as e: messagebox.showerror("失败", f"读取失败:\n{e.stderr}") return except json.JSONDecodeError: messagebox.showerror("失败", "解析授权文件输出失败") return self.name_var.set(data.get("name", "")) self.mc_var.set(data.get("machinecode", "")) self.phone_var.set(data.get("phone", "")) self.unit_var.set(data.get("unit", "")) self.auth_var.set(data.get("authdatetime", "")) self._parse_date(data.get("overdatetime", ""), self.over_year, self.over_month, self.over_day) self._parse_date(data.get("updatedatetime", ""), self.update_year, self.update_month, self.update_day) self.path_var.set(os.path.dirname(f) or ".") self._update_filename() messagebox.showinfo("成功", f"已加载授权文件:\n{f}") if __name__ == "__main__": LicenseGUI()