Files
Head_CT_Morph/video_generator_app.py

137 lines
4.6 KiB
Python

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=45,
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()