import threading from pathlib import Path from tkinter import ( DISABLED, HORIZONTAL, NORMAL, Button, Entry, Frame, Label, Scale, StringVar, Tk, filedialog, messagebox, ) from generate_head_extension_video import generate_video class VideoGeneratorApp: def __init__(self, root): self.root = root self.root.title("仰头角度变化视频生成工具") self.root.geometry("760x260") self.input_dir = StringVar(value="input_ct_2F") self.output_file = StringVar(value=str(Path("ppt_video") / "head_extension_0_to_20deg.mp4")) self.status = StringVar(value="请选择 DICOM 文件夹,然后点击生成视频。") self.angle_text = StringVar(value="20°") self.duration_text = StringVar(value="6 秒") self.build_ui() def build_ui(self): top = Frame(self.root) top.pack(fill="x", padx=12, pady=12) Label(top, text="DICOM 文件夹").grid(row=0, column=0, sticky="w") Entry(top, textvariable=self.input_dir, width=72).grid(row=0, column=1, padx=8) Button(top, text="选择", command=self.choose_input).grid(row=0, column=2) Label(top, text="输出视频").grid(row=1, column=0, sticky="w", pady=8) Entry(top, textvariable=self.output_file, width=72).grid(row=1, column=1, padx=8) Button(top, text="选择", command=self.choose_output).grid(row=1, column=2) controls = Frame(self.root) controls.pack(fill="x", padx=12, pady=4) Label(controls, text="最大仰头角度").grid(row=0, column=0, sticky="w") self.max_angle = Scale( controls, from_=5, to=30, orient=HORIZONTAL, resolution=1, length=360, command=self.on_angle_change, ) self.max_angle.set(20) self.max_angle.grid(row=0, column=1, padx=8) Label(controls, textvariable=self.angle_text, width=8, anchor="w").grid(row=0, column=2) Label(controls, text="动画时长").grid(row=1, column=0, sticky="w") self.duration = Scale( controls, from_=3, to=12, orient=HORIZONTAL, resolution=1, length=360, command=self.on_duration_change, ) self.duration.set(6) self.duration.grid(row=1, column=1, padx=8) Label(controls, textvariable=self.duration_text, width=8, anchor="w").grid(row=1, column=2) bottom = Frame(self.root) bottom.pack(fill="x", padx=12, pady=12) self.generate_button = Button(bottom, text="生成视频", width=18, command=self.start_generate) self.generate_button.pack(side="left") Label(bottom, textvariable=self.status, anchor="w").pack(side="left", padx=12) def choose_input(self): path = filedialog.askdirectory(title="选择 DICOM 文件夹") if path: self.input_dir.set(path) def choose_output(self): path = filedialog.asksaveasfilename( title="选择输出视频路径", defaultextension=".mp4", filetypes=[("MP4 视频", "*.mp4")], initialfile="head_extension_0_to_20deg.mp4", ) if path: self.output_file.set(path) def on_angle_change(self, value): self.angle_text.set(f"{float(value):.0f}°") def on_duration_change(self, value): self.duration_text.set(f"{float(value):.0f} 秒") def start_generate(self): self.generate_button.config(state=DISABLED) self.status.set("正在生成视频,请稍等...") thread = threading.Thread(target=self.generate_worker, daemon=True) thread.start() def generate_worker(self): try: output = generate_video( self.input_dir.get(), self.output_file.get(), float(self.max_angle.get()), float(self.duration.get()), ) self.root.after(0, lambda: self.status.set(f"完成:{output}")) self.root.after(0, lambda: messagebox.showinfo("完成", f"视频已生成:\n{output}")) except Exception as exc: error_message = str(exc) self.root.after(0, lambda: self.status.set("生成失败。")) self.root.after(0, lambda: messagebox.showerror("生成失败", error_message)) finally: self.root.after(0, lambda: self.generate_button.config(state=NORMAL)) def main(): root = Tk() VideoGeneratorApp(root) root.mainloop() if __name__ == "__main__": main()