python-script-library/可视化质检.py

389 lines
14 KiB
Python
Raw Permalink Normal View History

2025-10-28 16:02:55 +08:00
import os
import tkinter as tk
from tkinter import messagebox
import subprocess
import sys
import time
# =============== 全局命令配置 ===============
COMMANDS_TEMPLATE = [
"conda activate aiden-p3d",
"d:",
"cd aiden-p3d",
]
# 扫描路径配置默认F盘根目录
SCAN_PATH = "E:\\" # 修改为您需要的默认扫描路径
# 新增文件夹检测间隔(秒)
DETECTION_INTERVAL = 5 # 每5秒检测一次新文件夹
# 游戏配置映射
GAME_COMMANDS = {
"赛博朋克2077": {
"command": "python load_point_cloud_saibo_new.py \"{}\"",
"description": "赛博朋克2077 - python load_point_cloud_saibo_new.py"
},
"巫师3": {
"command": "python load_point_cloud_wushi_new.py \"{}\"",
"description": "巫师3 - python load_point_cloud_wushi_new.py"
},
"孤岛危机": {
"command": "python load_point_cloud_crysis_new.py \"{}\"",
"description": "孤岛危机 - python load_point_cloud_crysis_new.py"
},
"地平线零之曙光": {
"command": "python load_point_horizon_new.py \"{}\"",
"description": "地平线零之曙光 - python load_point_horizon_new.py"
},
"原子之心": {
"command": "python debug_atomicHeart.py -d \"{}\"",
"description": "原子之心 - python debug_atomicHeart.py -d"
},
"巴士模拟18": {
"command": "python debug_bus18.py -d \"{}\"",
"description": "巴士模拟18 - python debug_bus18.py -d"
},
"超自然车旅": {
"command": "python debug_pacificdrive.py -d \"{}\"",
"description": "超自然车旅 - python debug_pacificdrive.py -d"
},
"印蒂卡": {
"command": "python debug_indika.py -d \"{}\"",
"description": "印蒂卡 - python debug_indika.py -d"
},
"黑神话:悟空": {
"command": "python restruct_b1.py --data-dir \"{}\"",
"description": "黑神话:悟空 - python restruct_b1.py --data-dir"
},
"新游戏选项": {
"command": "conda activate worldgen && E: && cd datacheck && run.bat \"{}\"",
"description": "新游戏选项 - E: && cd datacheck && run.bat"
}
}
# MeshLab路径
MESHLAB_PATH = r"C:\Program Files\VCG\MeshLab\meshlab.exe"
# 输出目录
OUTPUT_DIR = r"E:\datacheck\outputs"
# =============== 结束配置 ===============
class FolderPlayerApp:
def __init__(self, root):
self.root = root
self.root.title("CV Saved Folders Player")
self.root.geometry("700x250") # 增加窗口宽度以适应新按钮
self.root.resizable(False, False)
self.selected_game = tk.StringVar(value="赛博朋克2077")
self.scan_folders()
if not self.folders:
messagebox.showerror("错误", f"在路径 {SCAN_PATH} 中没有找到以'cv_saved_''25'为前缀的文件夹")
sys.exit(1)
self.current_index = 0
self.process = None
self.last_scan_time = time.time()
self.create_ui()
self.update_display()
self.start_detection_timer()
self._force_focus_timer()
def _force_focus_timer(self):
self.root.focus_force()
self.root.after(500, self._force_focus_timer)
def start_detection_timer(self):
self.check_new_folders()
self.root.after(DETECTION_INTERVAL * 1000, self.start_detection_timer)
def check_new_folders(self):
current_dir = SCAN_PATH
try:
all_items = os.listdir(current_dir)
except Exception as e:
print(f"检测新文件夹时出错: {e}")
return
processed_folders = set()
for filename in ["passed_files.txt", "rejected_files.txt"]:
if os.path.exists(filename):
with open(filename, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#'):
processed_folders.add(line)
new_folders = [
os.path.join(current_dir, item)
for item in all_items
if ((item.startswith("cv_saved_") or item.startswith("25")) and
os.path.isdir(os.path.join(current_dir, item)) and
item not in processed_folders and
os.path.join(current_dir, item) not in self.folders)
]
if new_folders:
new_folders.sort()
was_empty = len(self.folders) == 0
self.folders.extend(new_folders)
if was_empty and self.folders:
self.current_index = 0
self.update_display()
messagebox.showinfo("提示", f"检测到 {len(new_folders)} 个新文件夹,已添加到列表")
def scan_folders(self):
current_dir = SCAN_PATH
try:
all_items = os.listdir(current_dir)
except FileNotFoundError:
messagebox.showerror("错误", f"指定的扫描路径不存在: {SCAN_PATH}")
sys.exit(1)
except PermissionError:
messagebox.showerror("错误", f"没有权限访问指定路径: {SCAN_PATH}")
sys.exit(1)
processed_folders = set()
for filename in ["passed_files.txt", "rejected_files.txt"]:
if os.path.exists(filename):
with open(filename, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#'):
processed_folders.add(line)
self.folders = [
os.path.join(current_dir, item)
for item in all_items
if ((item.startswith("cv_saved_") or item.startswith("25")) and
os.path.isdir(os.path.join(current_dir, item)) and
item not in processed_folders)
]
self.folders.sort()
def create_ui(self):
game_frame = tk.Frame(self.root)
game_frame.pack(fill=tk.X, padx=10, pady=5)
tk.Label(game_frame, text="选择游戏:", font=("Arial", 10)).pack(side=tk.LEFT)
game_dropdown = tk.OptionMenu(game_frame, self.selected_game, *GAME_COMMANDS.keys())
game_dropdown.pack(side=tk.LEFT, padx=5)
self.status_label = tk.Label(self.root, text="", pady=5)
self.status_label.pack()
self.folder_label = tk.Label(self.root, text="", font=("Arial", 12), fg="blue")
self.folder_label.pack()
button_frame = tk.Frame(self.root)
button_frame.pack(side=tk.BOTTOM, pady=20)
self.prev_button = tk.Button(
button_frame,
text="上一条(<-)",
width=10,
command=self.prev_folder,
state=tk.DISABLED if self.current_index == 0 else tk.NORMAL
)
self.prev_button.pack(side=tk.LEFT, padx=5)
self.play_button = tk.Button(
button_frame,
text="播放(空格)",
width=10,
command=self.play_current
)
self.play_button.pack(side=tk.LEFT, padx=5)
self.pass_button = tk.Button(
button_frame,
text="通过(p)",
width=10,
command=self.mark_as_passed,
bg="green",
fg="white"
)
self.pass_button.pack(side=tk.LEFT, padx=5)
self.reject_button = tk.Button(
button_frame,
text="不通过(r)",
width=10,
command=self.mark_as_rejected,
bg="red",
fg="white"
)
self.reject_button.pack(side=tk.LEFT, padx=5)
self.next_button = tk.Button(
button_frame,
text="下一条(->)",
width=10,
command=self.next_folder,
state=tk.DISABLED if self.current_index == len(self.folders) - 1 else tk.NORMAL
)
self.next_button.pack(side=tk.LEFT, padx=5)
# 新增打开pts.ply文件按钮
self.open_ply_button = tk.Button(
button_frame,
text="打开PLY(o)",
width=10,
command=self.open_ply_file,
bg="orange",
fg="white"
)
self.open_ply_button.pack(side=tk.LEFT, padx=5)
self.root.bind('<Left>', lambda event: self.prev_folder())
self.root.bind('<Right>', lambda event: self.next_folder())
self.root.bind('<space>', lambda event: self.play_current())
self.root.bind('<p>', lambda event: self.mark_as_passed())
self.root.bind('<r>', lambda event: self.mark_as_rejected())
self.root.bind('<o>', lambda event: self.open_ply_file()) # 绑定o键
def update_display(self):
if not self.folders:
self.status_label.config(text="所有文件夹已处理完毕")
self.folder_label.config(text="")
return
current_path = self.folders[self.current_index]
folder_name = os.path.basename(current_path)
self.status_label.config(text=f"正在处理: 第 {self.current_index + 1}/{len(self.folders)}")
self.folder_label.config(text=folder_name)
self.prev_button.config(state=tk.NORMAL if self.current_index > 0 else tk.DISABLED)
self.next_button.config(state=tk.NORMAL if self.current_index < len(self.folders) - 1 else tk.DISABLED)
def prev_folder(self):
if self.current_index > 0:
self.current_index -= 1
self.update_display()
def next_folder(self):
if self.current_index < len(self.folders) - 1:
self.current_index += 1
self.update_display()
def play_current(self):
if not self.folders:
return
current_path = self.folders[self.current_index]
selected_game_name = self.selected_game.get()
game_config = GAME_COMMANDS.get(selected_game_name)
if not game_config:
messagebox.showerror("错误", f"未找到游戏 {selected_game_name} 的配置")
return
try:
self.close_command_prompt()
time.sleep(0.5)
if selected_game_name == "新游戏选项":
# 执行新游戏选项命令不使用COMMANDS_TEMPLATE
game_command = game_config["command"]
formatted_command = game_command.format(current_path)
# 启动命令(不等待)
self.process = subprocess.Popen(f'start cmd /k "{formatted_command}"', shell=True)
else:
# 其他游戏使用原有的COMMANDS_TEMPLATE
game_command = game_config["command"]
formatted_command = game_command.format(current_path)
commands = COMMANDS_TEMPLATE.copy()
commands.append(formatted_command)
full_command = " && ".join(commands)
self.process = subprocess.Popen(f'start cmd /k "{full_command}"', shell=True)
except Exception as e:
messagebox.showerror("错误", f"执行命令时出错:\n{str(e)}")
def open_ply_file(self):
"""打开output目录中的pts.ply文件"""
ply_file = os.path.join(OUTPUT_DIR, "pts.ply")
if os.path.exists(ply_file):
try:
subprocess.Popen([MESHLAB_PATH, ply_file])
messagebox.showinfo("成功", f"已用MeshLab打开文件: {ply_file}")
except Exception as e:
messagebox.showerror("错误", f"打开文件时出错:\n{str(e)}")
else:
messagebox.showwarning("警告", f"未找到文件: {ply_file}")
def close_command_prompt(self):
try:
if self.process:
self.process.terminate()
subprocess.Popen('taskkill /f /im cmd.exe', shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
creationflags=subprocess.CREATE_NO_WINDOW)
except Exception as e:
print(f"关闭命令行时出错: {e}")
def mark_as_passed(self):
self._mark_file("passed_files.txt")
def mark_as_rejected(self):
self._mark_file("rejected_files.txt")
def _mark_file(self, filename):
if not self.folders:
return
current_path = self.folders[self.current_index]
folder_name = os.path.basename(current_path)
try:
if not os.path.exists(filename):
with open(filename, 'w', encoding='utf-8') as f:
pass
with open(filename, 'a', encoding='utf-8') as f:
if os.path.getsize(filename) == 0:
f.write(f"{folder_name}\n")
else:
needs_newline = True
with open(filename, 'rb+') as f_check:
f_check.seek(-1, os.SEEK_END)
last_char = f_check.read(1)
needs_newline = (last_char != b'\n')
if needs_newline:
f.write('\n')
f.write(f"{folder_name}\n")
status = "通过" if filename == "passed_files.txt" else "不通过"
messagebox.showinfo("成功", f"已标记为{status}: {folder_name}")
self.folders.pop(self.current_index)
if self.current_index >= len(self.folders):
if len(self.folders) > 0:
self.current_index = len(self.folders) - 1
else:
messagebox.showinfo("完成", "所有文件夹已处理完毕!")
self.update_display()
return
self.update_display()
except Exception as e:
messagebox.showerror("错误", f"写入文件时出错:\n{str(e)}")
if __name__ == "__main__":
root = tk.Tk()
app = FolderPlayerApp(root)
root.mainloop()