first commit
This commit is contained in:
136
video_generator_app.py
Normal file
136
video_generator_app.py
Normal file
@@ -0,0 +1,136 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user