| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- #!/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("<Return>", 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()
|