From 9a9d6fbe1072a822f5dbdf975c8b28f81884d1f2 Mon Sep 17 00:00:00 2001 From: Jeffreytsai1004 Date: Mon, 10 Feb 2025 03:19:08 +0800 Subject: [PATCH] Updated --- README.md | 37 ++-- scripts/ui/rigging.py | 499 +++++++++++++++++++++++++----------------- 2 files changed, 320 insertions(+), 216 deletions(-) diff --git a/README.md b/README.md index d2c9ec4..f388fe5 100644 --- a/README.md +++ b/README.md @@ -295,28 +295,27 @@ Reference\UI\17_Definitions.jpg 第四行:修复接缝(icons\fix_seam.png); -####### DNA预设浏览器 +###### 2. 绑定视图 (Rigging) -- 预设角色图标矩阵 (6x6网格) -- 缩放控制滑块 (默认90) -- 导入/导出设置按钮 +####### 预设 -####### 资产设置 +- DNA浏览器 +- 图标缩放滑条; - 导入/导出设置按钮 -- 项目路径设置 -- 预设文件选择 -- 数据分层选项 - - 行为(包括描述和定义) - - 覆盖表情选项 -- 描述设置: - - 名称:Archetype - - 原型:其他 - - 性别:女性 - - 年龄:24 - - 变换单位:厘米 - - 旋转单位:角度 - - 向上轴标:Z轴向上 - - LOD计数:8 +####### 资产 +- 项目路径: 输入框; 加载按钮 +- 预设文件: 输入框; 加载按钮 +- 数据分层: 下拉框(默认:行为(包括基础定义)); - 勾选框(勾选):覆盖表情 + +####### 描述 + - 名称:输入框(默认:Archetype); - 变换单位:下拉框(默认:厘米) + - 原型:下拉框(默认:亚洲人); - 旋转单位:下拉框(默认:角度) + - 性别:下拉框(默认:女性); - 向上轴标:下拉框(默认:Z轴向上) + - 年龄:数值调节(默认:24); - LOD计数:数值调节(默认:8) + +####### 功能按钮 + +- 清空选项(icons\delete.png); - 导入骨架(icons\HIKCharacterToolSkeleton.png); - 创建骨架(icons\HIKcreateControlRig.png); ###### 3. 行为视图 (Behaviour) diff --git a/scripts/ui/rigging.py b/scripts/ui/rigging.py index eda9e16..4e113f9 100644 --- a/scripts/ui/rigging.py +++ b/scripts/ui/rigging.py @@ -40,226 +40,331 @@ class RiggingTab(QtWidgets.QWidget): def _setup_ui(self): """设置UI布局""" # === Main Layout === - self.main_layout = QtWidgets.QHBoxLayout(self) - self.main_layout.setContentsMargins(2, 2, 2, 2) - self.main_layout.setSpacing(2) + self.main_layout = QtWidgets.QVBoxLayout(self) + self.main_layout.setContentsMargins(0, 0, 0, 0) + self.main_layout.setSpacing(0) - # 创建左右两个主面板 - self.dna_browser_widget = self._create_dna_browser() - self.asset_settings_widget = self._create_asset_settings() + # === DNA预览区域 === + self.preview_area = self._create_preview_area() - # 设置左右比例为1:1 - self.main_layout.addWidget(self.dna_browser_widget, 1) - self.main_layout.addWidget(self.asset_settings_widget, 1) + # === 设置区域 === + self.settings_area = QtWidgets.QWidget() + settings_layout = QtWidgets.QVBoxLayout(self.settings_area) + settings_layout.setContentsMargins(8, 8, 8, 8) + settings_layout.setSpacing(4) - def _create_dna_browser(self): - """创建DNA预设浏览器""" - # === Widget === - widget = QtWidgets.QWidget() - - # === Layout === - layout = QtWidgets.QVBoxLayout(widget) - layout.setContentsMargins(5, 5, 5, 5) - layout.setSpacing(5) - - # 预设网格视图 - self.preset_view = QtWidgets.QListWidget() - self.preset_view.setViewMode(QtWidgets.QListView.IconMode) - self.preset_view.setIconSize(QtCore.QSize(90, 90)) - self.preset_view.setSpacing(10) - self.preset_view.setResizeMode(QtWidgets.QListView.Adjust) - self.preset_view.setMovement(QtWidgets.QListView.Static) - self.preset_view.setWrapping(True) - layout.addWidget(self.preset_view) - - # 缩放控制 - zoom_widget = QtWidgets.QWidget() - zoom_layout = QtWidgets.QHBoxLayout(zoom_widget) - zoom_layout.setContentsMargins(0, 0, 0, 0) - - zoom_label = QtWidgets.QLabel("缩放:") - self.zoom_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal) - self.zoom_slider.setMinimum(60) - self.zoom_slider.setMaximum(200) - self.zoom_slider.setValue(90) - self.zoom_value_label = QtWidgets.QLabel("90") - - zoom_layout.addWidget(zoom_label) - zoom_layout.addWidget(self.zoom_slider) - zoom_layout.addWidget(self.zoom_value_label) - layout.addWidget(zoom_widget) - - # 导入/导出按钮 - button_widget = QtWidgets.QWidget() - button_layout = QtWidgets.QHBoxLayout(button_widget) - button_layout.setContentsMargins(0, 0, 0, 0) - - self.import_btn = QtWidgets.QPushButton("导入设置") - self.export_btn = QtWidgets.QPushButton("导出设置") - - button_layout.addWidget(self.import_btn) - button_layout.addWidget(self.export_btn) - layout.addWidget(button_widget) - - return widget - - def _create_asset_settings(self): - """创建资产设置面板""" - # === Widget === - widget = QtWidgets.QWidget() - - # === Layout === - layout = QtWidgets.QVBoxLayout(widget) - layout.setContentsMargins(5, 5, 5, 5) - layout.setSpacing(5) - - # 项目路径设置 - self.project_group = self._create_project_settings() - layout.addWidget(self.project_group) - - # 数据分层选项 - self.layer_group = self._create_layer_settings() - layout.addWidget(self.layer_group) - - # 描述设置 - self.desc_group = self._create_description_settings() - layout.addWidget(self.desc_group) - - layout.addStretch() - - return widget - - def _create_project_settings(self): - """创建项目设置组""" - # === Widget === - group = QtWidgets.QGroupBox("项目设置") - - # === Layout === - layout = QtWidgets.QFormLayout(group) - layout.setContentsMargins(5, 5, 5, 5) - layout.setSpacing(5) + # === 资产设置 === + asset_group = QtWidgets.QWidget() + asset_layout = QtWidgets.QGridLayout(asset_group) + asset_layout.setContentsMargins(0, 0, 0, 0) + asset_layout.setSpacing(4) # 项目路径 - path_widget = QtWidgets.QWidget() - path_layout = QtWidgets.QHBoxLayout(path_widget) - path_layout.setContentsMargins(0, 0, 0, 0) - - self.project_path = QtWidgets.QLineEdit() - self.browse_project_btn = QtWidgets.QPushButton("浏览...") - self.browse_project_btn.setFixedWidth(60) - - path_layout.addWidget(self.project_path) - path_layout.addWidget(self.browse_project_btn) - layout.addRow("项目路径:", path_widget) + path_label = QtWidgets.QLabel("项目路径:") + self.path_edit = QtWidgets.QLineEdit() + self.path_edit.setText("D:/Personal/Document/maya/SuperRiggingEditor/files/data/MetaHuman") + self.path_btn = QtWidgets.QPushButton("加载...") + self.path_btn.setIcon(QtGui.QIcon(":fileOpen.png")) # 使用Maya内置图标 + self.path_btn.setFixedWidth(60) # 调整按钮宽度 + self.path_btn.setFixedHeight(24) # 预设文件 - preset_widget = QtWidgets.QWidget() - preset_layout = QtWidgets.QHBoxLayout(preset_widget) - preset_layout.setContentsMargins(0, 0, 0, 0) + preset_label = QtWidgets.QLabel("预设文件:") + self.preset_edit = QtWidgets.QLineEdit() + self.preset_edit.setText("nal/Document/maya/SuperRiggingEditor/files/presets/metahuman/MH_Ada.dna") + self.preset_btn = QtWidgets.QPushButton("加载...") + self.preset_btn.setIcon(QtGui.QIcon(":fileOpen.png")) # 使用Maya内置图标 + self.preset_btn.setFixedWidth(60) # 调整按钮宽度 + self.preset_btn.setFixedHeight(24) - self.preset_path = QtWidgets.QLineEdit() - self.browse_preset_btn = QtWidgets.QPushButton("浏览...") - self.browse_preset_btn.setFixedWidth(60) + # 数据分层 + layer_label = QtWidgets.QLabel("数据分层:") + self.layer_combo = QtWidgets.QComboBox() + self.layer_combo.addItem("行为(包括基础定义)") + self.override_check = QtWidgets.QCheckBox("覆盖表情") + self.override_check.setChecked(True) + self.override_check.setStyleSheet(""" + QCheckBox { + background: transparent; + } + QCheckBox::indicator { + width: 16px; + height: 16px; + background: #3D3D3D; + border: 1px solid #555555; + border-radius: 2px; + } + QCheckBox::indicator:checked { + background: #3D3D3D; + image: url(:checkboxChecked.png); + } + QCheckBox::indicator:unchecked { + background: #3D3D3D; + image: url(:checkboxUnchecked.png); + } + QCheckBox::indicator:hover { + border: 1px solid #666666; + } + """) - preset_layout.addWidget(self.preset_path) - preset_layout.addWidget(self.browse_preset_btn) - layout.addRow("预设文件:", preset_widget) + # 添加到资产布局 + asset_layout.addWidget(path_label, 0, 0) + asset_layout.addWidget(self.path_edit, 0, 1) + asset_layout.addWidget(self.path_btn, 0, 2) + asset_layout.addWidget(preset_label, 1, 0) + asset_layout.addWidget(self.preset_edit, 1, 1) + asset_layout.addWidget(self.preset_btn, 1, 2) + asset_layout.addWidget(layer_label, 2, 0) + asset_layout.addWidget(self.layer_combo, 2, 1) + asset_layout.addWidget(self.override_check, 2, 2) - return group + # === 描述设置 === + desc_group = QtWidgets.QWidget() + desc_layout = QtWidgets.QGridLayout(desc_group) + desc_layout.setContentsMargins(0, 0, 0, 0) + desc_layout.setSpacing(4) - def _create_layer_settings(self): - """创建数据分层设置组""" - # === Widget === - group = QtWidgets.QGroupBox("数据分层") + # 设置列的拉伸因子,使左右两侧均等 + desc_layout.setColumnStretch(0, 0) # 左侧标签不拉伸 + desc_layout.setColumnStretch(1, 1) # 左侧控件拉伸 + desc_layout.setColumnStretch(2, 0) # 右侧标签不拉伸 + desc_layout.setColumnStretch(3, 1) # 右侧控件拉伸 - # === Layout === - layout = QtWidgets.QVBoxLayout(group) - layout.setContentsMargins(5, 5, 5, 5) - layout.setSpacing(5) + # 左侧设置 + left_settings = [ + ("名称:", "Archetype", "line"), + ("原型:", "其他", "combo"), + ("性别:", "女性", "combo"), + ("年龄:", "24", "spin") + ] - self.behavior_check = QtWidgets.QCheckBox("行为(包括描述和定义)") - self.expression_check = QtWidgets.QCheckBox("覆盖表情选项") + # 右侧设置 + right_settings = [ + ("变换单位:", "厘米", "combo"), + ("旋转单位:", "角度", "combo"), + ("向上轴:", "Z轴向上", "combo"), + ("LOD计数:", "8", "spin") + ] - layout.addWidget(self.behavior_check) - layout.addWidget(self.expression_check) + # 添加左侧设置 + for row, (label_text, default_value, control_type) in enumerate(left_settings): + label = QtWidgets.QLabel(label_text) + label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) # 标签左对齐 + label.setFixedWidth(50) # 固定标签宽度 + + if control_type == "line": + control = QtWidgets.QLineEdit(default_value) + elif control_type == "combo": + control = QtWidgets.QComboBox() + control.addItem(default_value) + elif control_type == "spin": + control = QtWidgets.QSpinBox() + control.setValue(int(default_value)) + + desc_layout.addWidget(label, row, 0) + desc_layout.addWidget(control, row, 1) - return group + # 添加右侧设置 + for row, (label_text, default_value, control_type) in enumerate(right_settings): + label = QtWidgets.QLabel(label_text) + label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) # 标签左对齐 + label.setFixedWidth(70) # 固定标签宽度,右侧标签略宽 + + if control_type == "line": + control = QtWidgets.QLineEdit(default_value) + elif control_type == "combo": + control = QtWidgets.QComboBox() + control.addItem(default_value) + elif control_type == "spin": + control = QtWidgets.QSpinBox() + control.setValue(int(default_value)) + + desc_layout.addWidget(label, row, 2) + desc_layout.addWidget(control, row, 3) - def _create_description_settings(self): - """创建描述设置组""" - # === Widget === - group = QtWidgets.QGroupBox("描述设置") + # 设置列间距 + desc_layout.setHorizontalSpacing(8) # 增加列之间的间距 - # === Layout === - layout = QtWidgets.QFormLayout(group) - layout.setContentsMargins(5, 5, 5, 5) - layout.setSpacing(5) + # === 底部按钮 === + button_group = QtWidgets.QWidget() + button_layout = QtWidgets.QHBoxLayout(button_group) + button_layout.setContentsMargins(8, 4, 8, 4) + button_layout.setSpacing(4) # 减小按钮间距 - # 创建设置项 - self.name_edit = QtWidgets.QLineEdit("Archetype") - self.type_combo = QtWidgets.QComboBox() - self.type_combo.addItem("其他") - self.gender_combo = QtWidgets.QComboBox() - self.gender_combo.addItem("女性") - self.age_spin = QtWidgets.QSpinBox() - self.age_spin.setValue(24) - self.transform_combo = QtWidgets.QComboBox() - self.transform_combo.addItem("厘米") - self.rotation_combo = QtWidgets.QComboBox() - self.rotation_combo.addItem("角度") - self.up_axis_combo = QtWidgets.QComboBox() - self.up_axis_combo.addItem("Z轴向上") - self.lod_spin = QtWidgets.QSpinBox() - self.lod_spin.setValue(8) + # 创建功能按钮 + self.clear_btn = self._create_tool_button("清空选项", "delete.png", "delete.png") + self.import_btn = self._create_tool_button("导入骨架", "HIKCharacterToolSkeleton.png", "kinJoint.png") + self.create_btn = self._create_tool_button("创建骨架", "HIKcreateControlRig.png", "kinHandle.png") - # 添加到布局 - layout.addRow("名称:", self.name_edit) - layout.addRow("原型:", self.type_combo) - layout.addRow("性别:", self.gender_combo) - layout.addRow("年龄:", self.age_spin) - layout.addRow("变换单位:", self.transform_combo) - layout.addRow("旋转单位:", self.rotation_combo) - layout.addRow("向上轴标:", self.up_axis_combo) - layout.addRow("LOD计数:", self.lod_spin) + # 设置按钮的大小策略 + for btn in [self.clear_btn, self.import_btn, self.create_btn]: + btn.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + btn.setMinimumHeight(32) + button_layout.addWidget(btn) + if btn != self.create_btn: + button_layout.addSpacing(4) - return group + # === 添加到主设置布局 === + settings_layout.addWidget(asset_group) + settings_layout.addWidget(desc_group) + settings_layout.addStretch() + settings_layout.addWidget(button_group) + # === 添加到主布局 === + self.main_layout.addWidget(self.preview_area, stretch=1) + self.main_layout.addWidget(self.settings_area) + + # === 设置样式 === + self._setup_style() + + def _create_preview_area(self): + """创建DNA预览区域""" + widget = QtWidgets.QWidget() + layout = QtWidgets.QVBoxLayout(widget) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + # 预览区域 - 单个大框 + preview_widget = QtWidgets.QFrame() + preview_widget.setStyleSheet(""" + QFrame { + background: #1D1D1D; + border: 1px solid #555555; + } + """) + preview_widget.setMinimumHeight(300) # 调整预览区域高度 + + # 底部控制区域 + control_widget = QtWidgets.QWidget() + control_layout = QtWidgets.QHBoxLayout(control_widget) + control_layout.setContentsMargins(8, 4, 8, 4) + control_layout.setSpacing(8) + + # 左侧数值显示和滑块 + value_widget = QtWidgets.QWidget() + value_layout = QtWidgets.QHBoxLayout(value_widget) + value_layout.setContentsMargins(0, 0, 0, 0) + value_layout.setSpacing(4) + + self.scale_value = QtWidgets.QLabel("90") + self.scale_value.setFixedWidth(30) + self.scale_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + + self.scale_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal) + self.scale_slider.setRange(0, 100) + self.scale_slider.setValue(90) + self.scale_slider.valueChanged.connect(self._on_scale_changed) # 添加滑块值变化事件 + + value_layout.addWidget(self.scale_value) + value_layout.addWidget(self.scale_slider) + + # 右侧按钮 + self.export_btn = QtWidgets.QPushButton("导出预设") + self.export_btn.setIcon(QtGui.QIcon(":save.png")) # 使用Maya内置图标 + self.export_btn.setFixedHeight(24) + self.export_btn.setMinimumWidth(80) # 设置最小宽度 + + self.import_btn = QtWidgets.QPushButton("导入预设") + self.import_btn.setIcon(QtGui.QIcon(":fileOpen.png")) # 使用Maya内置图标 + self.import_btn.setFixedHeight(24) + self.import_btn.setMinimumWidth(80) # 设置最小宽度 + + # 添加到控制布局 + # 设置这行高度 + control_layout.setContentsMargins(0, 0, 0, 0) + control_layout.setSpacing(4) + control_layout.addWidget(value_widget) + control_layout.addWidget(self.export_btn) + control_layout.addWidget(self.import_btn) + + + # 添加到主布局 + layout.addWidget(preview_widget) + layout.addWidget(control_widget) + + return widget + + def _create_tool_button(self, text, icon_name, fallback_icon=None): + """创建工具按钮""" + btn = QtWidgets.QPushButton(text) + + # 尝试使用自定义图标,如果失败则使用Maya内置图标 + icon_path = f"{config.ICONS_PATH}/{icon_name}" + if os.path.exists(icon_path): + btn.setIcon(QtGui.QIcon(icon_path)) + elif fallback_icon: + btn.setIcon(QtGui.QIcon(f":{fallback_icon}")) + + return btn + + def _setup_style(self): + """设置样式""" + self.setStyleSheet(""" + QWidget { + background: #2D2D2D; + color: #CCCCCC; + } + QLabel { + background: transparent; + } + QLineEdit, QComboBox { + background: #3D3D3D; + border: 1px solid #555555; + border-radius: 2px; + padding: 4px; + color: #CCCCCC; + min-height: 22px; + } + QPushButton { + background: #3D3D3D; + border: 1px solid #555555; + border-radius: 2px; + padding: 4px 8px; + color: #CCCCCC; + } + QPushButton:hover { + background: #454545; + } + QPushButton:pressed { + background: #2A2A2A; + } + QCheckBox { + spacing: 8px; + background: transparent; + } + QCheckBox::indicator { + width: 16px; + height: 16px; + } + QSlider::groove:horizontal { + border: 1px solid #555555; + height: 4px; + background: #3D3D3D; + margin: 0px; + border-radius: 2px; + } + QSlider::handle:horizontal { + background: #CCCCCC; + border: 1px solid #555555; + width: 12px; + height: 12px; + margin: -4px 0; + border-radius: 6px; + } + QSlider::handle:horizontal:hover { + background: #FFFFFF; + } + """) + + def _on_scale_changed(self, value): + """滑块值变化事件处理""" + self.scale_value.setText(str(value)) + def _create_connections(self): """创建信号连接""" - # 缩放滑块 - self.zoom_slider.valueChanged.connect(self._on_zoom_changed) - - # 导入导出按钮 - self.import_btn.clicked.connect(self._on_import) - self.export_btn.clicked.connect(self._on_export) - - # 浏览按钮 - self.browse_project_btn.clicked.connect(self._on_browse_project) - self.browse_preset_btn.clicked.connect(self._on_browse_preset) - - def _on_zoom_changed(self, value): - """缩放值改变""" - self.zoom_value_label.setText(str(value)) - self.preset_view.setIconSize(QtCore.QSize(value, value)) - - def _on_import(self): - """导入设置""" - # TODO: 实现导入逻辑 - pass - - def _on_export(self): - """导出设置""" - # TODO: 实现导出逻辑 - pass - - def _on_browse_project(self): - """浏览项目路径""" - # TODO: 实现浏览逻辑 - pass - - def _on_browse_preset(self): - """浏览预设文件""" - # TODO: 实现浏览逻辑 + # 滑块值变化事件 + self.scale_slider.valueChanged.connect(self._on_scale_changed) + # TODO: 添加信号连接 pass if __name__ == "__main__":