Update
This commit is contained in:
parent
816523347d
commit
c2324147ae
@ -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)
|
@ -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()
|
||||
|
@ -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,12 +531,15 @@ 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"""
|
||||
"""处理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']
|
||||
}
|
||||
|
||||
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)
|
||||
# ... 其余处理代码保持不变
|
||||
|
||||
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,48 +817,69 @@ 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:
|
||||
"""
|
||||
Creates and adds the DNA chooser widget
|
||||
@ -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'])
|
||||
|
Loading…
Reference in New Issue
Block a user