This commit is contained in:
Jeffreytsai1004 2025-02-06 06:29:55 +08:00
parent a6ffb3f3bd
commit a984f8a921
2 changed files with 234 additions and 65 deletions

View File

@ -32,88 +32,155 @@ except ImportError:
class ModelTab(BaseWidget): class ModelTab(BaseWidget):
"""模型标签页""" """模型标签页"""
def __init__(self, parent=None): def __init__(self, parent=None):
# 在super()之前初始化类属性
self.lod_tabs = None
self.lod_buttons = []
self.source_combo = None
self.target_combo = None
# 调用父类初始化
super(ModelTab, self).__init__(parent) super(ModelTab, self).__init__(parent)
def setup_ui(self): def setup_ui(self):
"""初始化UI"""
layout = QtWidgets.QVBoxLayout(self) layout = QtWidgets.QVBoxLayout(self)
layout.setSpacing(10)
layout.setContentsMargins(10, 10, 10, 10)
# 创建滚动区域 # 创建标签页控件
scroll = QtWidgets.QScrollArea() self.lod_tabs = QtWidgets.QTabWidget()
scroll.setWidgetResizable(True) layout.addWidget(self.lod_tabs)
scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
layout.addWidget(scroll)
# 创建内容控件 # 每个LOD级别的模型类型定义
content = QtWidgets.QWidget() lod_models = {
content_layout = QtWidgets.QVBoxLayout(content) 0: ["头部", "牙齿", "牙龈", "左眼", "右眼", "虹膜", "睫毛", "眼睑", "软骨", "身体"],
scroll.setWidget(content) 1: ["头部", "牙齿", "牙龈", "左眼", "右眼", "虹膜", "睫毛", "眼睑", "软骨", "身体"],
2: ["头部", "牙齿", "牙龈", "左眼", "右眼", "虹膜", "睫毛", "眼睑", "身体"],
3: ["头部", "牙齿", "左眼", "右眼", "虹膜", "睫毛", "眼睑", "身体"],
4: ["头部", "牙齿", "左眼", "右眼", "虹膜"],
5: ["头部", "牙齿", "左眼", "右眼"],
6: ["头部", "牙齿", "左眼", "右眼"],
7: ["头部", "牙齿", "左眼", "右眼"]
}
# 添加LOD组 # 创建LOD0-7标签页
for i in range(8): for i in range(8):
lod_group = LODGroup(i) tab = QtWidgets.QWidget()
content_layout.addWidget(lod_group) tab_layout = QtWidgets.QVBoxLayout(tab)
tab_layout.setSpacing(10)
# 添加LOD功能组 # 创建网格布局
grid = QtWidgets.QGridLayout()
grid.setSpacing(10)
# 获取当前LOD级别的模型类型
models = lod_models[i]
# 为每个模型类型创建控件
for idx, model in enumerate(models):
# 添加标签
label_widget = QtWidgets.QLabel(f"{model}:")
label_widget.setMinimumWidth(50)
grid.addWidget(label_widget, idx, 0)
# 添加输入框
line_edit = QtWidgets.QLineEdit()
line_edit.setReadOnly(True)
line_edit.setMinimumWidth(200)
grid.addWidget(line_edit, idx, 1)
# 添加加载按钮
btn = IconButton("target.png", "加载...")
btn.setMinimumWidth(80)
if i > 0: # LOD0以外的按钮默认禁用
btn.setEnabled(False)
grid.addWidget(btn, idx, 2)
self.lod_buttons.append(btn)
# 连接按钮信号
callback_name = f"load_{model.lower()}"
if hasattr(self, callback_name):
btn.clicked.connect(getattr(self, callback_name))
# 添加删除按钮
delete_btn = IconButton("delete.png", "")
delete_btn.clicked.connect(lambda x=i: self.delete_lod(x))
tab_layout.addWidget(delete_btn, alignment=QtCore.Qt.AlignRight)
tab_layout.addLayout(grid)
tab_layout.addStretch()
self.lod_tabs.addTab(tab, f"LOD{i}")
# LOD功能组
lod_tools = self.create_lod_tools() lod_tools = self.create_lod_tools()
content_layout.addWidget(lod_tools) layout.addWidget(lod_tools)
# 添加模型工具组 # 模型工具组
model_tools = self.create_model_tools() model_tools = self.create_model_tools()
content_layout.addWidget(model_tools) layout.addWidget(model_tools)
content_layout.addStretch()
def create_lod_tools(self): def create_lod_tools(self):
"""创建LOD功能组""" """创建LOD功能组"""
group = QtWidgets.QGroupBox("LOD功能") group = QtWidgets.QGroupBox("LOD功能")
layout = QtWidgets.QHBoxLayout(group) layout = QtWidgets.QHBoxLayout(group)
layout.setSpacing(5)
# 自定加载模型 # 创建三个按钮并设置为等宽
load_btn = IconButton("load_meshes.png", "自定加载模型") buttons = [
load_btn.clicked.connect(self.load_custom_models) ("自定义加载模型", "custom_load.png", self.load_custom_models),
layout.addWidget(load_btn) ("标准化命名", "standardize.png", self.standardize_naming),
("自动分组", "auto_group.png", self.auto_group)
]
# 标准化命名 for text, icon, callback in buttons:
name_btn = IconButton("standardized_naming.png", "标准化命名") btn = IconButton(icon, text)
name_btn.clicked.connect(self.standardize_naming) btn.clicked.connect(callback)
layout.addWidget(name_btn) btn.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Fixed)
# 自动分组 layout.addWidget(btn)
group_btn = IconButton("automatic_grouping.png", "自动分组")
group_btn.clicked.connect(self.auto_group)
layout.addWidget(group_btn)
layout.addStretch()
return group return group
def create_model_tools(self): def create_model_tools(self):
"""创建模型工具组""" """创建模型工具组"""
group = QtWidgets.QGroupBox("模型工具") group = QtWidgets.QGroupBox("模型工具")
layout = QtWidgets.QVBoxLayout(group) layout = QtWidgets.QVBoxLayout(group)
layout.setSpacing(10)
# 拓扑和LOD选择 # 第一行选择栏和创建LOD按钮
options = QtWidgets.QHBoxLayout() top_layout = QtWidgets.QHBoxLayout()
top_layout.setSpacing(10)
# 拓扑结构选择 # 源模型选择
options.addWidget(QtWidgets.QLabel("拓扑结构:")) source_layout = QtWidgets.QHBoxLayout()
topo_combo = QtWidgets.QComboBox() source_label = QtWidgets.QLabel("源模型:")
topo_combo.addItem("MetaHuman") source_label.setMinimumWidth(60)
options.addWidget(topo_combo) self.source_combo = QtWidgets.QComboBox()
self.source_combo.addItems(["选择源模型..."])
self.source_combo.setMinimumWidth(120)
source_layout.addWidget(source_label)
source_layout.addWidget(self.source_combo)
top_layout.addLayout(source_layout)
# LOD选择 # 目标LOD选择
options.addWidget(QtWidgets.QLabel("选择LOD:")) target_layout = QtWidgets.QHBoxLayout()
lod_combo = QtWidgets.QComboBox() target_label = QtWidgets.QLabel("目标LOD:")
lod_combo.addItem("全部") target_label.setMinimumWidth(60)
for i in range(8): self.target_combo = QtWidgets.QComboBox()
lod_combo.addItem(f"LOD{i}") self.target_combo.addItems([f"LOD{i}" for i in range(8)])
options.addWidget(lod_combo) self.target_combo.setMinimumWidth(120)
target_layout.addWidget(target_label)
target_layout.addWidget(self.target_combo)
top_layout.addLayout(target_layout)
options.addStretch() # 创建LOD按钮
layout.addLayout(options) create_lod_btn = IconButton("create_lod.png", "创建LOD")
create_lod_btn.clicked.connect(self.create_lod)
create_lod_btn.setMinimumWidth(100)
top_layout.addWidget(create_lod_btn)
# 工具按钮 layout.addLayout(top_layout)
tools = QtWidgets.QHBoxLayout()
# 其他工具按钮,每行两个
tool_buttons = [ tool_buttons = [
("模型分离", "polySplitVertex.png", self.split_model), ("模型分离", "polySplitVertex.png", self.split_model),
("生成面部配件", "supplement_meshes.png", self.generate_facial_accessories), ("生成面部配件", "supplement_meshes.png", self.generate_facial_accessories),
@ -122,16 +189,30 @@ class ModelTab(BaseWidget):
("修复接缝", "polyChipOff.png", self.fix_seams) ("修复接缝", "polyChipOff.png", self.fix_seams)
] ]
for text, icon, callback in tool_buttons: # 创建网格布局
grid = QtWidgets.QGridLayout()
grid.setSpacing(10)
for idx, (text, icon, callback) in enumerate(tool_buttons):
btn = IconButton(icon, text) btn = IconButton(icon, text)
btn.clicked.connect(callback) btn.clicked.connect(callback)
tools.addWidget(btn) btn.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Fixed)
tools.addStretch() grid.addWidget(btn, idx//2, idx%2)
layout.addLayout(tools)
layout.addLayout(grid)
return group return group
def create_lod(self):
"""创建LOD"""
# 获取选中的源模型和目标LOD
source_model = self.source_combo.currentText()
target_lod = int(self.target_combo.currentText().replace("LOD", ""))
# 启用目标LOD的所有按钮
for btn in self.lod_buttons:
if btn.parent().parent() == self.lod_tabs.widget(target_lod):
btn.setEnabled(True)
# LOD功能回调 # LOD功能回调
def load_custom_models(self): def load_custom_models(self):
"""自定加载模型""" """自定加载模型"""
@ -172,4 +253,88 @@ class ModelTab(BaseWidget):
def fix_seams(self): def fix_seams(self):
"""修复接缝""" """修复接缝"""
from scripts.utils import model_utils from scripts.utils import model_utils
model_utils.fix_seams() model_utils.fix_seams()
def load_head(self, lod_index):
"""加载头部模型"""
from scripts.utils import model_utils
file_path = self._get_file_path("头部模型")
if file_path:
model_utils.load_model(lod_index, "head", file_path)
self._update_input_text(lod_index, "头部", file_path)
def load_teeth(self, lod_index):
"""加载牙齿模型"""
from scripts.utils import model_utils
file_path = self._get_file_path("牙齿模型")
if file_path:
model_utils.load_model(lod_index, "teeth", file_path)
self._update_input_text(lod_index, "牙齿", file_path)
def load_gums(self, lod_index):
"""加载牙龈模型"""
from scripts.utils import model_utils
file_path = self._get_file_path("牙龈模型")
if file_path:
model_utils.load_model(lod_index, "gums", file_path)
self._update_input_text(lod_index, "牙龈", file_path)
def delete_lod(self, lod_index):
"""删除指定LOD"""
from scripts.utils import model_utils
reply = QtWidgets.QMessageBox.question(
self,
"删除LOD",
f"确定要删除LOD{lod_index}吗?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
)
if reply == QtWidgets.QMessageBox.Yes:
model_utils.delete_lod(lod_index)
# 清空输入框
self._clear_lod_inputs(lod_index)
# 禁用按钮
if lod_index > 0:
self._disable_lod_buttons(lod_index)
def _get_file_path(self, model_type):
"""获取文件路径"""
file_path, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
f"选择{model_type}",
"",
"模型文件 (*.obj *.fbx *.ma *.mb);;所有文件 (*.*)"
)
return file_path
def _update_input_text(self, lod_index, model_type, file_path):
"""更新输入框文本"""
tab = self.lod_tabs.widget(lod_index)
if tab:
grid = tab.layout().itemAt(1).layout() # 获取网格布局
for i in range(grid.rowCount()):
label = grid.itemAtPosition(i, 0).widget()
if label.text().startswith(model_type):
input_field = grid.itemAtPosition(i, 1).widget()
input_field.setText(os.path.basename(file_path))
break
def _clear_lod_inputs(self, lod_index):
"""清空LOD的所有输入框"""
tab = self.lod_tabs.widget(lod_index)
if tab:
grid = tab.layout().itemAt(1).layout()
for i in range(grid.rowCount()):
input_field = grid.itemAtPosition(i, 1).widget()
if input_field:
input_field.clear()
def _disable_lod_buttons(self, lod_index):
"""禁用LOD的所有按钮"""
tab = self.lod_tabs.widget(lod_index)
if tab:
grid = tab.layout().itemAt(1).layout()
for i in range(grid.rowCount()):
btn = grid.itemAtPosition(i, 2).widget()
if btn:
btn.setEnabled(False)

View File

@ -169,18 +169,22 @@ class LODGroup(QtWidgets.QGroupBox):
class IconButton(QtWidgets.QPushButton): class IconButton(QtWidgets.QPushButton):
"""图标按钮""" """图标按钮"""
def __init__(self, icon_name="", tooltip="", parent=None): def __init__(self, icon_name, text=""):
super(IconButton, self).__init__(parent) super(IconButton, self).__init__()
self.setup_ui(icon_name, tooltip) self.setText(text)
def setup_ui(self, icon_name, tooltip):
if icon_name: if icon_name:
icon_path = os.path.join(data.ICONS_PATH, icon_name) icon_path = os.path.join(data.ICONS_PATH, icon_name)
if os.path.exists(icon_path): if os.path.exists(icon_path):
self.setIcon(QtGui.QIcon(icon_path)) self.setIcon(QtGui.QIcon(icon_path))
if tooltip: self.setIconSize(QtCore.QSize(24, 24)) # 设置图标大小
self.setToolTip(tooltip) self.setMinimumHeight(32) # 设置最小高度
self.setFixedSize(30, 30) # 设置文字在图标右侧
self.setStyleSheet("""
QPushButton {
text-align: left;
padding-left: 5px;
}
""")
class SliderWithValue(QtWidgets.QWidget): class SliderWithValue(QtWidgets.QWidget):
"""带数值显示的滑块""" """带数值显示的滑块"""