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

389 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()