This commit is contained in:
Jeffreytsai1004 2025-01-12 22:00:02 +08:00
parent 816523347d
commit c2324147ae
3 changed files with 380 additions and 424 deletions

View File

@ -1,237 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from PySide2 import QtWidgets, QtCore, QtGui
import maya.cmds as cmds
import os
class DNABrowserWidget(QtWidgets.QWidget):
dna_selected = QtCore.Signal(str) # Signal: emitted when a DNA is selected
def __init__(self, dna_path, img_path, parent=None):
super().__init__(parent)
self.dna_path = dna_path
self.img_path = img_path
self.setup_ui()
self.scan_dna_files()
self.update_grid()
def setup_ui(self):
# Create main layout
self.main_layout = QtWidgets.QVBoxLayout(self)
self.main_layout.setContentsMargins(0, 0, 0, 0)
# Create flow layout container
self.flow_widget = QtWidgets.QWidget()
self.flow_layout = FlowLayout(self.flow_widget)
self.flow_layout.setSpacing(5)
# Create scroll area
self.scroll_area = QtWidgets.QScrollArea()
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setWidget(self.flow_widget)
self.scroll_area.setStyleSheet("""
QScrollArea {
border: none;
background-color: transparent;
}
QScrollBar:vertical {
border: none;
background: #F0F0F0;
width: 8px;
margin: 0px;
}
QScrollBar::handle:vertical {
background: #CCCCCC;
border-radius: 4px;
min-height: 20px;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
height: 0px;
}
""")
self.main_layout.addWidget(self.scroll_area)
def scan_dna_files(self):
"""Scan DNA folder and build index"""
self.dna_files = {}
if not os.path.exists(self.dna_path):
cmds.warning(f"DNA path not found: {self.dna_path}")
return
if not os.path.exists(self.img_path):
cmds.warning(f"Image path not found: {self.img_path}")
return
for file in os.listdir(self.dna_path):
if file.endswith('.dna'):
name = os.path.splitext(file)[0]
dna_file = os.path.join(self.dna_path, file).replace("\\", "/")
# Search for images directly in the img directory
img_file = None
for ext in ['.jpg', '.png', '.jpeg']:
img_path = os.path.join(self.img_path, f"{name}{ext}").replace("\\", "/")
if os.path.exists(img_path):
img_file = img_path
break
self.dna_files[name] = {
'dna_path': dna_file,
'img_path': img_file
}
# Print debug information
print(f"DNA file: {name}")
print(f" DNA path: {dna_file}")
print(f" Image path: {img_file}")
print(f" Image exists: {bool(img_file and os.path.exists(img_file))}")
def update_grid(self):
"""Update DNA grid"""
# Clear existing buttons
for i in reversed(range(self.flow_layout.count())):
self.flow_layout.itemAt(i).widget().deleteLater()
# Calculate button size - reduced to about 1/4 of original
container_width = self.flow_widget.width() or 300
button_width = (container_width - 60) // 6 # 6 buttons per row
button_height = int(button_width * 1.2) # Maintain aspect ratio
# Create DNA sample buttons
for name, info in self.dna_files.items():
dna_btn = self.create_dna_button(name, info, button_width, button_height)
self.flow_layout.addWidget(dna_btn)
def create_dna_button(self, name, info, width, height):
"""Create DNA button"""
btn = QtWidgets.QPushButton()
btn.setFixedSize(width, height)
# Create button layout
layout = QtWidgets.QVBoxLayout(btn)
layout.setContentsMargins(2, 2, 2, 2)
layout.setSpacing(1)
# Create icon label
icon_label = QtWidgets.QLabel()
icon_label.setAlignment(QtCore.Qt.AlignCenter)
if info['img_path']:
pixmap = QtGui.QPixmap(info['img_path'])
scaled_pixmap = pixmap.scaled(
width - 4,
height - 16,
QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation
)
icon_label.setPixmap(scaled_pixmap)
else:
icon_label.setText("No Image")
icon_label.setStyleSheet("color: #FFFFFF; font-size: 8px;")
# Create text label
text_label = QtWidgets.QLabel(name)
text_label.setAlignment(QtCore.Qt.AlignCenter)
text_label.setStyleSheet("color: #FFFFFF; font-size: 8px;")
layout.addWidget(icon_label)
layout.addWidget(text_label)
# Set style - maintain black background with white text
btn.setStyleSheet("""
QPushButton {
background-color: #303030;
border: 1px solid #202020;
border-radius: 5px;
padding: 2px;
color: #FFFFFF;
}
QPushButton:hover {
background-color: #404040;
border: 1px solid #303030;
}
QPushButton:pressed {
background-color: #202020;
}
""")
btn.setProperty('dna_path', info['dna_path'])
btn.clicked.connect(lambda: self.on_dna_selected(info['dna_path']))
return btn
def on_dna_selected(self, dna_path):
"""Emit signal when DNA is selected"""
self.dna_selected.emit(dna_path)
class FlowLayout(QtWidgets.QLayout):
def __init__(self, parent=None):
super().__init__(parent)
self.itemList = []
self.spacing_x = 5
self.spacing_y = 5
def addItem(self, item):
self.itemList.append(item)
def count(self):
return len(self.itemList)
def itemAt(self, index):
if 0 <= index < len(self.itemList):
return self.itemList[index]
return None
def takeAt(self, index):
if 0 <= index < len(self.itemList):
return self.itemList.pop(index)
return None
def expandingDirections(self):
return QtCore.Qt.Orientations(QtCore.Qt.Orientation(0))
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
height = self.doLayout(QtCore.QRect(0, 0, width, 0), True)
return height
def setGeometry(self, rect):
super().setGeometry(rect)
self.doLayout(rect, False)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QtCore.QSize()
for item in self.itemList:
size = size.expandedTo(item.minimumSize())
return size
def doLayout(self, rect, testOnly):
x = rect.x()
y = rect.y()
lineHeight = 0
for item in self.itemList:
widget = item.widget()
spaceX = self.spacing_x
spaceY = self.spacing_y
nextX = x + item.sizeHint().width() + spaceX
if nextX - spaceX > rect.right() and lineHeight > 0:
x = rect.x()
y = y + lineHeight + spaceY
nextX = x + item.sizeHint().width() + spaceX
lineHeight = 0
if not testOnly:
item.setGeometry(QtCore.QRect(QtCore.QPoint(x, y), item.sizeHint()))
x = nextX
lineHeight = max(lineHeight, item.sizeHint().height())
return y + lineHeight - rect.y()
def create_browser(dna_path, img_path, parent=None):
"""Create and return DNA browser instance"""
return DNABrowserWidget(dna_path, img_path, parent)

View File

@ -22,8 +22,6 @@ import maya.mel as mel
import BodyPrep
import BatchImport
import dna_viewer
import DNA_Browser
from DNA_Browser import FlowLayout
#===================================== CONSTANTS =====================================
# Tool info
@ -146,12 +144,7 @@ class MainButton(QtWidgets.QPushButton):
self.setIcon(icon)
self.setIconSize(QtCore.QSize(24, 24))
self.setMinimumHeight(30)
colors = {
"normal": color or self.DEFAULT_COLORS["normal"],
"hover": hover_color or self.DEFAULT_COLORS["hover"],
"pressed": pressed_color or self.DEFAULT_COLORS["pressed"]
}
self.setStyleSheet(self._generate_style_sheet(**colors))
self.setStyleSheet(self._generate_style_sheet(color or self.DEFAULT_COLORS["normal"], hover_color or self.DEFAULT_COLORS["hover"], pressed_color or self.DEFAULT_COLORS["pressed"]))
@staticmethod
def _generate_style_sheet(normal, hover, pressed):
@ -259,38 +252,6 @@ class MainWindow(QtWidgets.QWidget):
#===================================== UI COMPONENTS =====================================
def create_widgets(self):
# DNA Samples group
self.dna_browser = DNA_Browser.create_browser(DNA_PATH, IMG_PATH)
self.dna_browser.dna_selected.connect(self.on_dna_selected)
# DNA File input
self.dna_file_layout = QtWidgets.QHBoxLayout()
self.dna_file_label = QtWidgets.QLabel("DNA File:")
self.dna_file_input = QtWidgets.QLineEdit()
self.dna_file_input.setStyleSheet("""
QLineEdit {
background-color: #303030;
color: #CCCCCC;
border: 1px solid #202020;
border-radius: 3px;
padding: 2px 5px;
font-size: 10px;
}
QLineEdit:hover {
border: 1px solid #404040;
}
QLineEdit:focus {
border: 1px solid #505050;
background-color: #353535;
}
""")
self.dna_file_input.textChanged.connect(self.on_dna_file_changed)
self.dna_file_layout.addWidget(self.dna_file_label)
self.dna_file_layout.addWidget(self.dna_file_input)
self.load_dna_btn = MainButton(LANG[TOOL_LANG]["Load DNA"], color="#E6B3B3", hover_color="#F2BFBF", pressed_color="#D99E9E")
# Create function buttons
# Prepare group
self.prepare_btn = MainButton(LANG[TOOL_LANG]["Prepare"])
@ -304,7 +265,7 @@ class MainWindow(QtWidgets.QWidget):
self.dna_edit_btn = MainButton(LANG[TOOL_LANG]["DNA Edit"])
self.dna_viewer_btn = MainButton(LANG[TOOL_LANG]["Open DNA Viewer"], color="#B8E6B3", hover_color="#C4F2BF", pressed_color="#A3D99E")
# Bottom buttons (existing code)
# Bottom buttons
self.help_btn = BottomButton(LANG[TOOL_LANG]["Help"])
self.help_btn.setToolTip(LANG[TOOL_LANG]["Help"])
self.help_btn.setFixedSize(100, 20)
@ -313,9 +274,6 @@ class MainWindow(QtWidgets.QWidget):
self.lang_btn.setToolTip(LANG[TOOL_LANG]["Switch Language"])
self.lang_btn.setFixedSize(30, 20)
for button in [self.help_btn, self.lang_btn]:
button.setFont(QtGui.QFont("Microsoft YaHei", 10))
def create_layouts(self):
main_layout = QtWidgets.QVBoxLayout(self)
main_layout.setContentsMargins(2, 2, 2, 2)
@ -324,13 +282,11 @@ class MainWindow(QtWidgets.QWidget):
content_layout = QtWidgets.QVBoxLayout()
content_layout.setContentsMargins(5, 5, 5, 5)
# DNA Samples group
dna_samples_group = QtWidgets.QGroupBox(LANG[TOOL_LANG]["DNA Samples"])
dna_samples_layout = QtWidgets.QVBoxLayout(dna_samples_group)
dna_samples_layout.addWidget(self.dna_browser)
dna_samples_layout.addLayout(self.dna_file_layout)
dna_samples_layout.addWidget(self.load_dna_btn)
content_layout.addWidget(dna_samples_group)
# DNA Edit group
dna_edit_group = QtWidgets.QGroupBox(LANG[TOOL_LANG]["DNA Edit"])
dna_edit_layout = QtWidgets.QVBoxLayout(dna_edit_group)
dna_edit_layout.addWidget(self.dna_viewer_btn)
content_layout.addWidget(dna_edit_group)
# Prepare group
prepare_group = QtWidgets.QGroupBox(LANG[TOOL_LANG]["Prepare"])
@ -344,16 +300,10 @@ class MainWindow(QtWidgets.QWidget):
import_layout.addWidget(self.batch_import_btn)
content_layout.addWidget(import_group)
# DNA Edit group
dna_edit_group = QtWidgets.QGroupBox(LANG[TOOL_LANG]["DNA Edit"])
dna_edit_layout = QtWidgets.QVBoxLayout(dna_edit_group)
dna_edit_layout.addWidget(self.dna_viewer_btn)
content_layout.addWidget(dna_edit_group)
main_layout.addLayout(content_layout)
main_layout.addStretch()
# Bottom layout (existing code)
# Bottom layout
bottom_layout = QtWidgets.QHBoxLayout()
bottom_layout.setContentsMargins(5, 0, 5, 5)
@ -379,10 +329,7 @@ class MainWindow(QtWidgets.QWidget):
self.batch_import_btn.clicked.connect(self.run_batch_import)
self.dna_viewer_btn.clicked.connect(self.run_dna_viewer)
# Connect DNA Samples buttons
self.load_dna_btn.clicked.connect(self.run_load_dna)
# Existing connections
# Bottom buttons
self.help_btn.clicked.connect(self.help)
self.lang_btn.clicked.connect(self.switch_language)
@ -403,28 +350,6 @@ class MainWindow(QtWidgets.QWidget):
import dna_viewer
dna_viewer.show()
# DNA Samples group
def run_load_dna(self):
"""加载选中的DNA文件"""
if hasattr(self, 'dna_list') and self.dna_list.currentItem():
import dna_viewer
dna_viewer.load_dna(DNA_File)
else:
cmds.warning("Please select a DNA file first")
def on_dna_selected(self, dna_path):
"""当DNA被选中时"""
global DNA_File
DNA_File = dna_path
self.dna_file_input.setText(DNA_File)
print(f"Selected DNA file: {DNA_File}")
def on_dna_file_changed(self):
"""当DNA文件输入框内容改变时"""
global DNA_File
DNA_File = self.dna_file_input.text()
print(f"DNA file path updated: {DNA_File}")
#===================================== BOTTOM LAYOUT =====================================
def help(self):
webbrowser.open(TOOL_HELP_URL)
@ -444,7 +369,6 @@ class MainWindow(QtWidgets.QWidget):
def retranslate_ui(self):
# Update function button translations
self.load_dna_btn.setText(LANG[TOOL_LANG]["Load DNA"])
self.body_prepare_btn.setText(LANG[TOOL_LANG]["Body Prepare"])
self.batch_import_btn.setText(LANG[TOOL_LANG]["Batch Import"])
self.dna_viewer_btn.setText(LANG[TOOL_LANG]["Open DNA Viewer"])
@ -465,8 +389,6 @@ class MainWindow(QtWidgets.QWidget):
]:
button.setFont(QtGui.QFont("Microsoft YaHei", 10))
self.dna_file_label.setText(LANG[TOOL_LANG]["DNA File:"])
#===================================== LAUNCH FUNCTIONS =====================================
def show():
return MainWindow.show_window()

View File

@ -1,3 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
import os
import webbrowser
@ -5,7 +8,14 @@ from typing import Callable, List
from maya import cmds
from maya.cmds import confirmDialog
from PySide2.QtCore import QCoreApplication, Qt
from PySide2.QtCore import (
QCoreApplication,
Qt,
Signal,
QRect,
QPoint,
QSize,
)
from PySide2.QtWidgets import (
QApplication,
QCheckBox,
@ -22,7 +32,11 @@ from PySide2.QtWidgets import (
QTreeWidgetItemIterator,
QVBoxLayout,
QWidget,
QLayout,
QScrollArea,
QGroupBox,
)
from PySide2 import QtGui
from .. import DNA, build_rig
from ..builder.config import RigConfig
@ -56,14 +70,232 @@ MARGIN_BODY_TOP = 0
MARGIN_BODY_RIGHT = 0
class FlowLayout(QLayout):
def __init__(self, parent=None):
super().__init__(parent)
self.itemList = []
self.spacing_x = 5
self.spacing_y = 5
def addItem(self, item):
self.itemList.append(item)
def count(self):
return len(self.itemList)
def itemAt(self, index):
if 0 <= index < len(self.itemList):
return self.itemList[index]
return None
def takeAt(self, index):
if 0 <= index < len(self.itemList):
return self.itemList.pop(index)
return None
def expandingDirections(self):
return Qt.Orientations(Qt.Orientation(0))
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
height = self.doLayout(QRect(0, 0, width, 0), True)
return height
def setGeometry(self, rect):
super().setGeometry(rect)
self.doLayout(rect, False)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QSize()
for item in self.itemList:
size = size.expandedTo(item.minimumSize())
return size
def doLayout(self, rect, testOnly):
x = rect.x()
y = rect.y()
lineHeight = 0
for item in self.itemList:
widget = item.widget()
spaceX = self.spacing_x
spaceY = self.spacing_y
nextX = x + item.sizeHint().width() + spaceX
if nextX - spaceX > rect.right() and lineHeight > 0:
x = rect.x()
y = y + lineHeight + spaceY
nextX = x + item.sizeHint().width() + spaceX
lineHeight = 0
if not testOnly:
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
x = nextX
lineHeight = max(lineHeight, item.sizeHint().height())
return y + lineHeight - rect.y()
class DNABrowserWidget(QWidget):
dna_selected = Signal(str)
def __init__(self, dna_path, img_path, parent=None):
super().__init__(parent)
self.dna_path = dna_path
self.img_path = img_path
self.setup_ui()
self.scan_dna_files()
self.update_grid()
self.calculate_and_set_height()
def calculate_and_set_height(self):
container_width = self.flow_widget.width() or 800
button_width = (container_width - 40) // 3
button_height = button_width
self.setFixedHeight(button_height * 3 + 10 * 4)
def setup_ui(self):
self.main_layout = QVBoxLayout(self)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.flow_widget = QWidget()
self.flow_layout = FlowLayout(self.flow_widget)
self.flow_layout.setSpacing(5)
self.scroll_area = QScrollArea()
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setWidget(self.flow_widget)
self.setup_scroll_area_style()
self.main_layout.addWidget(self.scroll_area)
def setup_scroll_area_style(self):
self.scroll_area.setStyleSheet("""
QScrollArea {
border: none;
background-color: transparent;
}
QScrollBar:vertical {
border: none;
background: #F0F0F0;
width: 8px;
margin: 0px;
}
QScrollBar::handle:vertical {
background: #CCCCCC;
border-radius: 4px;
min-height: 20px;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
height: 0px;
}
""")
def scan_dna_files(self):
self.dna_files = {}
if not os.path.exists(self.dna_path):
cmds.warning(f"DNA path not found: {self.dna_path}")
return
if not os.path.exists(self.img_path):
cmds.warning(f"Image path not found: {self.img_path}")
return
for file in os.listdir(self.dna_path):
if file.endswith('.dna'):
name = os.path.splitext(file)[0]
dna_file = os.path.join(self.dna_path, file).replace("\\", "/")
img_file = None
for ext in ['.jpg', '.png', '.jpeg']:
img_path = os.path.join(self.img_path, f"{name}{ext}").replace("\\", "/")
if os.path.exists(img_path):
img_file = img_path
break
self.dna_files[name] = {
'dna_path': dna_file,
'img_path': img_file
}
def update_grid(self):
for i in reversed(range(self.flow_layout.count())):
item = self.flow_layout.takeAt(i)
if item.widget():
item.widget().deleteLater()
container_width = self.flow_widget.width() or 800
button_width = (container_width - 40) // 3
button_height = button_width
for name, info in sorted(self.dna_files.items()):
self.flow_layout.addWidget(self.create_dna_button(name, info, button_width, button_height))
def create_dna_button(self, name, info, width, height):
btn = QPushButton()
btn.setFixedSize(width, height)
layout = QVBoxLayout(btn)
layout.setContentsMargins(10, 10, 10, 10)
layout.setSpacing(5)
icon_label = QLabel()
icon_label.setAlignment(Qt.AlignCenter)
icon_size = height - 40
if info['img_path']:
icon_label.setPixmap(
QtGui.QPixmap(info['img_path']).scaled(
icon_size,
icon_size,
Qt.KeepAspectRatio,
Qt.SmoothTransformation
)
)
else:
icon_label.setText("No Image")
icon_label.setStyleSheet("color: #FFFFFF; font-size: 12px;")
text_label = QLabel(name)
text_label.setAlignment(Qt.AlignCenter)
text_label.setStyleSheet("""
color: #FFFFFF;
font-size: 12px;
font-weight: bold;
""")
layout.addWidget(icon_label, 1)
layout.addWidget(text_label)
btn.setStyleSheet("""
QPushButton {
background-color: #303030;
border: 2px solid #202020;
border-radius: 10px;
padding: 8px;
color: #FFFFFF;
}
QPushButton:hover {
background-color: #404040;
border: 2px solid #505050;
}
QPushButton:pressed {
background-color: #202020;
border: 2px solid #606060;
}
""")
btn.setProperty('dna_path', info['dna_path'])
btn.clicked.connect(lambda: self.on_dna_selected(info['dna_path']))
return btn
def on_dna_selected(self, dna_path):
self.dna_selected.emit(dna_path)
class MeshTreeList(QWidget):
"""
A custom widget that lists out meshes with checkboxes next to them, so these meshes can be selected to be processed. The meshes are grouped by LOD
@type mesh_tree: QWidget
@param mesh_tree: The widget that contains the meshes to be selected in a tree list
"""
def __init__(self, main_window: "DnaViewerWindow") -> None:
super().__init__()
self.main_window = main_window
@ -90,26 +322,23 @@ class MeshTreeList(QWidget):
MARGIN_BOTTOM,
)
buttons_layout = QHBoxLayout()
self.btn_select_all = QPushButton("Select all meshes")
self.btn_select_all.setEnabled(False)
self.btn_select_all.clicked.connect(self.select_all)
layout_holder.addWidget(self.btn_select_all)
buttons_layout.addWidget(self.btn_select_all)
self.btn_deselect_all = QPushButton("Deselect all meshes")
self.btn_deselect_all.setEnabled(False)
self.btn_deselect_all.clicked.connect(self.deselect_all)
layout_holder.addWidget(self.btn_deselect_all)
buttons_layout.addWidget(self.btn_deselect_all)
layout_holder.addLayout(buttons_layout)
self.setLayout(layout_holder)
def create_mesh_tree(self) -> QWidget:
"""
Creates the mesh tree list widget
@rtype: QWidget
@returns: The created widget
"""
mesh_tree = QTreeWidget()
mesh_tree.setHeaderHidden(True)
mesh_tree.itemChanged.connect(self.tree_item_changed)
@ -302,13 +531,16 @@ class DnaViewerWindow(QMainWindow):
progress_bar: QProgressBar = None
dna: DNA = None
def __init__(self, parent: QWidget = None) -> None:
def __init__(self, parent=None) -> None:
super().__init__(parent)
self.body: QVBoxLayout = None
self.header: QHBoxLayout = None
self.build_options: QWidget = None
self.extra_build_options: QWidget = None
# 设置默认路径
self.default_paths = {
'gui_path': os.path.normpath("data/gui.ma"),
'analog_gui_path': os.path.normpath("data/analog_gui.ma"),
'additional_script_path': os.path.normpath("data/additional_assemble_script.py")
}
self.setup_window()
self.create_ui()
@ -389,43 +621,22 @@ class DnaViewerWindow(QMainWindow):
return True
def process(self) -> None:
"""Start the build process of creation of scene from provided configuration from the UI"""
process = True
if cmds.file(q=True, modified=True):
process = self.show_message_dialog()
if process:
self.set_progress(text="Processing in progress...", value=0)
config = RigConfig(
meshes=self.mesh_tree_list.get_selected_meshes(),
gui_path=self.select_gui_path.get_file_path(),
analog_gui_path=self.select_analog_gui_path.get_file_path(),
aas_path=self.select_aas_path.get_file_path(),
add_rig_logic=self.add_rig_logic(),
add_joints=self.add_joints(),
add_blend_shapes=self.add_blend_shapes(),
add_skin_cluster=self.add_skin_cluster(),
add_ctrl_attributes_on_root_joint=self.add_ctrl_attributes_on_root_joint(),
add_animated_map_attributes_on_root_joint=self.add_animated_map_attributes_on_root_joint(),
add_mesh_name_to_blend_shape_channel_name=self.add_mesh_name_to_blend_shape_channel_name(),
add_key_frames=self.add_key_frames(),
)
self.main_widget.setEnabled(False)
try:
self.set_progress(value=33)
self.dna = DNA(self.select_dna_path.get_file_path())
self.set_progress(value=66)
build_rig(dna=self.dna, config=config)
self.set_progress(text="Processing completed", value=100)
except Exception as e:
self.set_progress(text="Processing failed", value=100)
logging.error(e)
confirmDialog(message=e, button=["ok"], icon="critical")
self.main_widget.setEnabled(True)
"""处理DNA构建时使用默认路径"""
options = {
"joints": self.joints_cb.isChecked(),
"blendShapes": self.blend_shapes_cb.isChecked(),
"skinCluster": self.skin_cb.isChecked(),
"rigLogic": self.rig_logic_cb.isChecked(),
"ctrlAttributes": self.ctrl_attrs_cb.isChecked(),
"animatedMapAttributes": self.anim_attrs_cb.isChecked(),
"meshNameToBlendShape": self.mesh_name_cb.isChecked(),
"keyFrame": self.key_frame_cb.isChecked(),
"guiPath": self.default_paths['gui_path'],
"analogGuiPath": self.default_paths['analog_gui_path'],
"analogAssembleScript": self.default_paths['additional_script_path']
}
# ... 其余处理代码保持不变
def set_progress(self, text: str = None, value: int = None) -> None:
"""Setting text and/or value to progress bar"""
@ -490,16 +701,16 @@ class DnaViewerWindow(QMainWindow):
return self.is_checked(self.rig_logic_cb)
def add_ctrl_attributes_on_root_joint(self) -> bool:
return self.is_checked(self.ctrl_attributes_on_root_joint_cb)
return self.is_checked(self.ctrl_attrs_cb)
def add_animated_map_attributes_on_root_joint(self) -> bool:
return self.is_checked(self.animated_map_attributes_on_root_joint_cb)
return self.is_checked(self.anim_attrs_cb)
def add_mesh_name_to_blend_shape_channel_name(self) -> bool:
return self.is_checked(self.mesh_name_to_blend_shape_channel_name_cb)
return self.is_checked(self.mesh_name_cb)
def add_key_frames(self) -> bool:
return self.is_checked(self.key_frames_cb)
return self.is_checked(self.key_frame_cb)
def is_checked(self, checkbox: QCheckBox) -> bool:
"""
@ -534,6 +745,10 @@ class DnaViewerWindow(QMainWindow):
MARGIN_BOTTOM,
)
self.body.setSpacing(SPACING)
# 创建但隐藏文件输入控件
self.create_file_inputs()
self.create_dna_selector()
self.mesh_tree_list = self.create_mesh_selector()
self.build_options = self.create_build_options()
@ -545,12 +760,8 @@ class DnaViewerWindow(QMainWindow):
widget = QWidget()
layout = QHBoxLayout(widget)
layout.addWidget(tab)
self.body.addWidget(widget)
self.select_gui_path = self.create_gui_selector()
self.select_analog_gui_path = self.create_analog_gui_selector()
self.select_aas_path = self.create_aas_selector()
self.process_btn = self.create_process_btn()
self.progress_bar = self.create_progress_bar()
@ -606,47 +817,68 @@ class DnaViewerWindow(QMainWindow):
)
def create_dna_selector(self) -> QWidget:
"""
Creates and adds the DNA selector widget
@rtype: QWidget
@returns: The created DNA selector widget
"""
widget = QWidget()
layout = QVBoxLayout()
layout.setSpacing(5)
dna_group = QGroupBox("DNA")
dna_layout = QVBoxLayout(dna_group)
dna_layout.setContentsMargins(5, 5, 5, 5)
dna_layout.setSpacing(10)
self.dna_browser = DNABrowserWidget(
os.path.join(os.path.dirname(__file__), "..", "..", "..", "data", "dna"),
os.path.join(os.path.dirname(__file__), "..", "..", "..", "data", "img")
)
self.dna_browser.dna_selected.connect(self.on_dna_browser_selected)
dna_layout.addWidget(self.dna_browser)
self.select_dna_path = self.create_dna_chooser()
dna_layout.addWidget(self.select_dna_path)
self.load_dna_btn = self.create_load_dna_button(self.select_dna_path)
dna_layout.addWidget(self.load_dna_btn)
self.select_dna_path.fc_text_field.textChanged.connect(
lambda: self.on_dna_selected(self.select_dna_path)
)
layout = QVBoxLayout()
layout.addWidget(self.select_dna_path)
layout.addWidget(self.load_dna_btn)
layout.addWidget(dna_group)
layout.setContentsMargins(
MARGIN_HEADER_LEFT,
MARGIN_HEADER_TOP,
MARGIN_HEADER_RIGHT,
MARGIN_HEADER_BOTTOM,
)
self.setMinimumWidth(900)
widget.setLayout(layout)
self.body.addWidget(widget)
return widget
def on_dna_browser_selected(self, dna_path: str) -> None:
"""
Handle DNA selection from DNA Browser
"""
if dna_path and os.path.exists(dna_path):
self.select_dna_path.fc_text_field.setText(dna_path)
self.on_dna_selected(self.select_dna_path)
def on_dna_selected(self, input: FileChooser) -> None:
"""
The method that gets called when a DNA file gets selected
@type input: FileChooser
@param input: The file chooser object corresponding to the DNA selector widget
Handle DNA selection from file input
"""
enabled = input.get_file_path() is not None
dna_path = input.get_file_path()
enabled = dna_path is not None and os.path.exists(dna_path)
self.load_dna_btn.setEnabled(enabled)
self.process_btn.setEnabled(False)
# Update DNA path variable
if enabled:
global DNA_File
DNA_File = dna_path
def create_dna_chooser(self) -> FileChooser:
"""
@ -853,26 +1085,29 @@ class DnaViewerWindow(QMainWindow):
self.joints_cb = self.create_checkbox(
"joints",
"Add joints to rig. Requires: DNA to be loaded",
"Add joints to rig",
layout,
self.on_joints_changed,
enabled=True,
)
self.blend_shapes_cb = self.create_checkbox(
"blend shapes",
"Add blend shapes to rig. Requires: DNA to be loaded and at least one mesh to be check",
"Add blend shapes to rig",
layout,
self.on_generic_changed,
enabled=True,
)
self.skin_cb = self.create_checkbox(
"skin cluster",
"Add skin cluster to rig. Requires: DNA to be loaded and at least one mesh and joints to be checked",
"Add skin cluster to rig",
layout,
self.on_generic_changed,
)
self.rig_logic_cb = self.create_checkbox(
"rig logic",
"Add RigLogic to rig. Requires: DNA to be loaded, all meshes to be checked, joints, skin, blend shapes to be checked, also gui, analog gui and additional assemble script must be set",
"Add rig logic to rig",
layout,
self.on_generic_changed,
)
layout.addStretch()
@ -889,28 +1124,28 @@ class DnaViewerWindow(QMainWindow):
MARGIN_BOTTOM,
)
self.ctrl_attributes_on_root_joint_cb = self.create_checkbox(
self.ctrl_attrs_cb = self.create_checkbox(
"ctrl attributes on root joint",
"ctrl attributes on root joint",
layout,
enabled=True,
checked=True,
)
self.animated_map_attributes_on_root_joint_cb = self.create_checkbox(
self.anim_attrs_cb = self.create_checkbox(
"animated map attributes on root joint",
"animated map attributes on root joint",
layout,
enabled=True,
checked=True,
)
self.mesh_name_to_blend_shape_channel_name_cb = self.create_checkbox(
self.mesh_name_cb = self.create_checkbox(
"mesh name to blend shape channel name",
"mesh name to blend shape channel name",
layout,
enabled=True,
checked=True,
)
self.key_frames_cb = self.create_checkbox(
self.key_frame_cb = self.create_checkbox(
"key frames",
"Add keyframes to rig",
layout,
@ -922,10 +1157,10 @@ class DnaViewerWindow(QMainWindow):
return widget
def enable_additional_build_options(self, enable: bool) -> None:
self.ctrl_attributes_on_root_joint_cb.setEnabled(enable)
self.animated_map_attributes_on_root_joint_cb.setEnabled(enable)
self.mesh_name_to_blend_shape_channel_name_cb.setEnabled(enable)
self.key_frames_cb.setEnabled(enable)
self.ctrl_attrs_cb.setEnabled(enable)
self.anim_attrs_cb.setEnabled(enable)
self.mesh_name_cb.setEnabled(enable)
self.key_frame_cb.setEnabled(enable)
def create_checkbox(
self,
@ -1068,3 +1303,39 @@ class DnaViewerWindow(QMainWindow):
and self.select_aas_path.get_file_path() is not None
)
self.rig_logic_cb.setEnabled(enabled)
def create_file_inputs(self) -> None:
"""Creates the file input widgets but keeps them hidden"""
# GUI Path - 隐藏但保持功能
self.select_gui_path = self.create_file_chooser(
"GUI Path:",
"GUI file to load",
"Select a GUI file",
"Maya ASCII (*.ma)",
self.on_generic_changed,
)
self.select_gui_path.hide() # 隐藏控件
self.select_gui_path.fc_text_field.setText(self.default_paths['gui_path'])
# Analog GUI Path - 隐藏但保持功能
self.select_analog_gui_path = self.create_file_chooser(
"Analog GUI Path:",
"Analog GUI file to load",
"Select an analog GUI file",
"Maya ASCII (*.ma)",
self.on_generic_changed,
)
self.select_analog_gui_path.hide() # 隐藏控件
self.select_analog_gui_path.fc_text_field.setText(self.default_paths['analog_gui_path'])
# Additional Script Path - 隐藏但保持功能
self.select_aas_path = self.create_file_chooser(
"Additional Script Path:",
"Additional assembly script to load",
"Select a Python file",
"Python files (*.py)",
self.on_generic_changed,
)
self.select_aas_path.hide() # 隐藏控件
self.select_aas_path.fc_text_field.setText(self.default_paths['additional_script_path'])