diff --git a/scripts/DNA_Browser.py b/scripts/DNA_Browser.py deleted file mode 100644 index 64dafe3..0000000 --- a/scripts/DNA_Browser.py +++ /dev/null @@ -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) diff --git a/scripts/MetaFusion.py b/scripts/MetaFusion.py index 29e4816..33382c3 100644 --- a/scripts/MetaFusion.py +++ b/scripts/MetaFusion.py @@ -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() diff --git a/scripts/dna_viewer/ui/app.py b/scripts/dna_viewer/ui/app.py index 64342c1..e15ca0a 100644 --- a/scripts/dna_viewer/ui/app.py +++ b/scripts/dna_viewer/ui/app.py @@ -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'])