Update
This commit is contained in:
@@ -73,6 +73,10 @@ class RiggingUI(ui_utils.BaseUI):
|
|||||||
self.main_widget = QtWidgets.QWidget()
|
self.main_widget = QtWidgets.QWidget()
|
||||||
self.main_widget.setObjectName("riggingMainWidget")
|
self.main_widget.setObjectName("riggingMainWidget")
|
||||||
|
|
||||||
|
# 初始化DNA文件列表和按钮尺寸
|
||||||
|
self.dna_files = []
|
||||||
|
self.dna_button_size = (120, 150)
|
||||||
|
|
||||||
# 初始化UI
|
# 初始化UI
|
||||||
self.create_widgets()
|
self.create_widgets()
|
||||||
self.create_layouts()
|
self.create_layouts()
|
||||||
@@ -81,6 +85,29 @@ class RiggingUI(ui_utils.BaseUI):
|
|||||||
# 更新UI文本
|
# 更新UI文本
|
||||||
self.update_language()
|
self.update_language()
|
||||||
|
|
||||||
|
# 加载DNA文件和预览图 - 移到最后执行确保所有控件已创建
|
||||||
|
self.load_dna_files()
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
"""
|
||||||
|
窗口关闭事件处理
|
||||||
|
清理缓存和资源
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 清理图片缓存
|
||||||
|
from scripts.utils import utils_rigging
|
||||||
|
utils_rigging.clear_pixmap_cache()
|
||||||
|
|
||||||
|
# 调用父类的关闭事件
|
||||||
|
super().closeEvent(event)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"窗口关闭处理失败: {str(e)}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
# 确保窗口正常关闭
|
||||||
|
event.accept()
|
||||||
|
|
||||||
#========================================= WIDGET =======================================
|
#========================================= WIDGET =======================================
|
||||||
def create_widgets(self):
|
def create_widgets(self):
|
||||||
"""
|
"""
|
||||||
@@ -121,47 +148,6 @@ class RiggingUI(ui_utils.BaseUI):
|
|||||||
self.controls["presets_flow_layout"].setContentsMargins(5, 5, 5, 5)
|
self.controls["presets_flow_layout"].setContentsMargins(5, 5, 5, 5)
|
||||||
self.controls["presets_flow_layout"].setSpacing(10)
|
self.controls["presets_flow_layout"].setSpacing(10)
|
||||||
|
|
||||||
# 添加测试预设项
|
|
||||||
for i in range(6): # 添加6个预设项,如图中所示
|
|
||||||
col = i % 6
|
|
||||||
row = i // 6
|
|
||||||
|
|
||||||
preset_widget = QtWidgets.QWidget()
|
|
||||||
preset_widget.setObjectName(f"preset_{i}")
|
|
||||||
preset_widget.setMinimumSize(120, 150) # 设置预设项大小
|
|
||||||
preset_widget.setMaximumSize(120, 150)
|
|
||||||
|
|
||||||
# 预设布局
|
|
||||||
preset_layout = QtWidgets.QVBoxLayout(preset_widget)
|
|
||||||
preset_layout.setContentsMargins(0, 0, 0, 0)
|
|
||||||
preset_layout.setSpacing(2)
|
|
||||||
|
|
||||||
# 预设图片
|
|
||||||
preset_image = QtWidgets.QLabel()
|
|
||||||
preset_image.setObjectName(f"preset_image_{i}")
|
|
||||||
preset_image.setMinimumSize(120, 120)
|
|
||||||
preset_image.setMaximumSize(120, 120)
|
|
||||||
preset_image.setScaledContents(True)
|
|
||||||
preset_image.setStyleSheet("background-color: #333333; border: 1px solid #555555;")
|
|
||||||
|
|
||||||
# 加载测试图片
|
|
||||||
pixmap = QtGui.QPixmap(os.path.join(ASSETS_PATH, "metahuman_placeholder.png"))
|
|
||||||
if not pixmap.isNull():
|
|
||||||
preset_image.setPixmap(pixmap)
|
|
||||||
|
|
||||||
# 预设标签
|
|
||||||
preset_label = QtWidgets.QLabel("METAHUMAN")
|
|
||||||
preset_label.setObjectName(f"preset_label_{i}")
|
|
||||||
preset_label.setAlignment(QtCore.Qt.AlignCenter)
|
|
||||||
preset_label.setStyleSheet("color: white; background-color: rgba(0, 0, 0, 128);")
|
|
||||||
|
|
||||||
# 添加到布局
|
|
||||||
preset_layout.addWidget(preset_image)
|
|
||||||
preset_layout.addWidget(preset_label)
|
|
||||||
|
|
||||||
# 添加到流布局
|
|
||||||
self.controls["presets_flow_layout"].addWidget(preset_widget, row, col)
|
|
||||||
|
|
||||||
# 设置滚动区域内容
|
# 设置滚动区域内容
|
||||||
self.controls["presets_scroll_area"].setWidget(self.controls["presets_content"])
|
self.controls["presets_scroll_area"].setWidget(self.controls["presets_content"])
|
||||||
|
|
||||||
@@ -171,7 +157,7 @@ class RiggingUI(ui_utils.BaseUI):
|
|||||||
self.controls["presets_slider_layout"].setSpacing(5)
|
self.controls["presets_slider_layout"].setSpacing(5)
|
||||||
|
|
||||||
# 数量显示
|
# 数量显示
|
||||||
self.controls["presets_count_label"] = QtWidgets.QLabel("99")
|
self.controls["presets_count_label"] = QtWidgets.QLabel("0")
|
||||||
self.controls["presets_count_label"].setObjectName("presetsCountLabel")
|
self.controls["presets_count_label"].setObjectName("presetsCountLabel")
|
||||||
self.controls["presets_count_label"].setAlignment(QtCore.Qt.AlignCenter)
|
self.controls["presets_count_label"].setAlignment(QtCore.Qt.AlignCenter)
|
||||||
self.controls["presets_count_label"].setStyleSheet("font-weight: bold;")
|
self.controls["presets_count_label"].setStyleSheet("font-weight: bold;")
|
||||||
@@ -486,14 +472,15 @@ class RiggingUI(ui_utils.BaseUI):
|
|||||||
# 设置分割器的伸缩因子
|
# 设置分割器的伸缩因子
|
||||||
for i in range(self.splitters["main_splitter"].count()):
|
for i in range(self.splitters["main_splitter"].count()):
|
||||||
self.splitters["main_splitter"].setStretchFactor(i, 1)
|
self.splitters["main_splitter"].setStretchFactor(i, 1)
|
||||||
|
|
||||||
|
# 监听预设内容区域大小变化,更新DNA网格布局
|
||||||
|
self.controls["presets_content"].resizeEvent = lambda event: utils_rigging.on_presets_content_resize(event, self)
|
||||||
|
|
||||||
#======================================= FUNCTIONS ======================================
|
#======================================= FUNCTIONS ======================================
|
||||||
def create_connections(self):
|
def create_connections(self):
|
||||||
"""
|
"""
|
||||||
创建信号连接,设置UI控件的交互行为
|
创建信号连接,设置UI控件的交互行为
|
||||||
"""
|
"""
|
||||||
# 导入绑定工具函数
|
|
||||||
from scripts.utils import utils_rigging
|
|
||||||
|
|
||||||
# 预设导入和导出按钮
|
# 预设导入和导出按钮
|
||||||
self.buttons["export_presets"].clicked.connect(utils_rigging.export_dna)
|
self.buttons["export_presets"].clicked.connect(utils_rigging.export_dna)
|
||||||
@@ -506,4 +493,25 @@ class RiggingUI(ui_utils.BaseUI):
|
|||||||
# 底部按钮连接
|
# 底部按钮连接
|
||||||
self.buttons["remove_all"].clicked.connect(utils_rigging.remove_all)
|
self.buttons["remove_all"].clicked.connect(utils_rigging.remove_all)
|
||||||
self.buttons["import_skeleton"].clicked.connect(utils_rigging.import_skeleton)
|
self.buttons["import_skeleton"].clicked.connect(utils_rigging.import_skeleton)
|
||||||
self.buttons["build_rigging"].clicked.connect(utils_rigging.build_rigging)
|
self.buttons["build_rigging"].clicked.connect(utils_rigging.build_rigging)
|
||||||
|
|
||||||
|
def load_dna_files(self):
|
||||||
|
"""
|
||||||
|
加载DNA文件和预览
|
||||||
|
在所有UI控件创建完成后执行
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 从utils_rigging加载DNA文件和预览图
|
||||||
|
from scripts.utils import utils_rigging
|
||||||
|
self.dna_files = utils_rigging.get_dna_files()
|
||||||
|
|
||||||
|
# 更新预设数量标签
|
||||||
|
if 'presets_count_label' in self.controls:
|
||||||
|
self.controls['presets_count_label'].setText(str(len(self.dna_files)))
|
||||||
|
|
||||||
|
# 调用utils_rigging中的加载预览按钮函数
|
||||||
|
utils_rigging.load_dna_preview_buttons(self)
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
print(f"加载DNA文件失败: {str(e)}")
|
||||||
|
traceback.print_exc()
|
@@ -20,6 +20,34 @@ QLabel#mainTitleLabel {
|
|||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==================== DNA预览按钮样式 ==================== */
|
||||||
|
QPushButton.dna-preview-button {
|
||||||
|
background-color: #2D2D30;
|
||||||
|
border: 1px solid #3E3E42;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton.dna-preview-button:hover {
|
||||||
|
border: 1px solid #007ACC;
|
||||||
|
background-color: #3E3E42;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton.dna-preview-button:pressed {
|
||||||
|
border: 1px solid #0062A3;
|
||||||
|
background-color: #1E1E1E;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton.dna-preview-button QLabel {
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-size: 11px;
|
||||||
|
border-radius: 0;
|
||||||
|
border-bottom-left-radius: 7px;
|
||||||
|
border-bottom-right-radius: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ==================== 标签页样式 ==================== */
|
/* ==================== 标签页样式 ==================== */
|
||||||
QTabWidget::pane {
|
QTabWidget::pane {
|
||||||
border: 1px solid #1E1E1E;
|
border: 1px solid #1E1E1E;
|
||||||
|
@@ -57,6 +57,477 @@ original_controller_properties = {}
|
|||||||
|
|
||||||
#========================================== FUNCTIONS ========================================
|
#========================================== FUNCTIONS ========================================
|
||||||
|
|
||||||
|
#------------------------------------ DNA PREVIEW ------------------------------------
|
||||||
|
def get_dna_files():
|
||||||
|
"""
|
||||||
|
获取所有DNA文件及其对应的预览图
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: 包含DNA文件信息的字典列表,每个字典包含:
|
||||||
|
- name: DNA文件名(不含扩展名)
|
||||||
|
- dna_path: DNA文件的完整路径
|
||||||
|
- image_path: 预览图的完整路径
|
||||||
|
"""
|
||||||
|
dna_files = []
|
||||||
|
default_preview = os.path.join(DNA_IMG_PATH, "Preview.png")
|
||||||
|
|
||||||
|
# 调试信息
|
||||||
|
print(f"加载DNA文件目录: {DNA_FILE_PATH}")
|
||||||
|
print(f"加载DNA图片目录: {DNA_IMG_PATH}")
|
||||||
|
|
||||||
|
# 确保目录存在
|
||||||
|
if not os.path.exists(DNA_FILE_PATH):
|
||||||
|
print(f"错误: DNA文件目录不存在: {DNA_FILE_PATH}")
|
||||||
|
return dna_files
|
||||||
|
|
||||||
|
if not os.path.exists(DNA_IMG_PATH):
|
||||||
|
print(f"错误: DNA图片目录不存在: {DNA_IMG_PATH}")
|
||||||
|
|
||||||
|
if not os.path.exists(default_preview):
|
||||||
|
print(f"错误: 默认预览图不存在: {default_preview}")
|
||||||
|
|
||||||
|
# 列出所有DNA文件
|
||||||
|
try:
|
||||||
|
dna_file_list = os.listdir(DNA_FILE_PATH)
|
||||||
|
print(f"找到 {len(dna_file_list)} 个文件在 DNA目录")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"列出DNA文件失败: {str(e)}")
|
||||||
|
return dna_files
|
||||||
|
|
||||||
|
# 获取所有可用的预览图文件名(不带扩展名),只查询一次目录提高性能
|
||||||
|
available_images = set()
|
||||||
|
try:
|
||||||
|
img_files = os.listdir(DNA_IMG_PATH)
|
||||||
|
for img_file in img_files:
|
||||||
|
if img_file.lower().endswith('.png'):
|
||||||
|
base_name = os.path.splitext(img_file)[0]
|
||||||
|
available_images.add(base_name)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"列出图片文件失败: {str(e)}")
|
||||||
|
|
||||||
|
# 处理每个DNA文件
|
||||||
|
for file_name in dna_file_list:
|
||||||
|
if file_name.lower().endswith('.dna'):
|
||||||
|
# 获取不含扩展名的文件名
|
||||||
|
name = os.path.splitext(file_name)[0]
|
||||||
|
dna_path = os.path.join(DNA_FILE_PATH, file_name).replace("\\", "/")
|
||||||
|
|
||||||
|
# 检查预览图是否存在,不重复检查文件系统提高性能
|
||||||
|
if name in available_images:
|
||||||
|
image_path = os.path.join(DNA_IMG_PATH, f"{name}.png").replace("\\", "/")
|
||||||
|
else:
|
||||||
|
image_path = default_preview
|
||||||
|
print(f"未找到预览图: {name}.png, 使用默认预览图")
|
||||||
|
|
||||||
|
dna_files.append({
|
||||||
|
"name": name,
|
||||||
|
"dna_path": dna_path,
|
||||||
|
"image_path": image_path
|
||||||
|
})
|
||||||
|
|
||||||
|
print(f"成功加载 {len(dna_files)} 个DNA文件")
|
||||||
|
return dna_files
|
||||||
|
|
||||||
|
def load_selected_dna(dna_path, ui_instance=None):
|
||||||
|
"""
|
||||||
|
加载用户选择的DNA文件
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dna_path (str): DNA文件路径
|
||||||
|
ui_instance: UI实例,用于更新UI状态
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否成功加载
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not os.path.exists(dna_path):
|
||||||
|
print(f"DNA文件不存在: {dna_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"加载DNA文件: {dna_path}")
|
||||||
|
|
||||||
|
# 更新UI中的DNA路径输入框(如果有UI实例)
|
||||||
|
if ui_instance and hasattr(ui_instance, 'controls') and 'presets_dna_input' in ui_instance.controls:
|
||||||
|
ui_instance.controls['presets_dna_input'].setText(dna_path)
|
||||||
|
|
||||||
|
# 这里可以添加实际的DNA文件加载逻辑
|
||||||
|
# 例如通过MetaHuman DNA Calibration库读取DNA文件
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"加载DNA文件失败: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def calculate_grid_layout(parent_width, item_width, spacing):
|
||||||
|
"""
|
||||||
|
根据父容器宽度计算每行可以容纳的项目数量
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parent_width (int): 父容器宽度
|
||||||
|
item_width (int): 每个项目的宽度
|
||||||
|
spacing (int): 项目间的间距
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: 每行可容纳的项目数量
|
||||||
|
"""
|
||||||
|
if parent_width <= 0 or item_width <= 0:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# 计算每行可容纳的项目数量
|
||||||
|
available_width = parent_width - spacing # 减去左边距
|
||||||
|
items_per_row = max(1, int(available_width / (item_width + spacing)))
|
||||||
|
|
||||||
|
return items_per_row
|
||||||
|
|
||||||
|
# 预缓存的图片缓存,减少重复加载
|
||||||
|
_pixmap_cache = {}
|
||||||
|
_pixmap_cache_max_size = 50 # 最多缓存50张图片
|
||||||
|
_pixmap_cache_lru = [] # 最近最少使用列表,用于清理缓存
|
||||||
|
|
||||||
|
def get_pixmap(image_path, width, height):
|
||||||
|
"""
|
||||||
|
从缓存获取QPixmap或创建新的
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_path: 图片路径
|
||||||
|
width: 所需宽度
|
||||||
|
height: 所需高度
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
QPixmap: 图片对象
|
||||||
|
"""
|
||||||
|
cache_key = f"{image_path}_{width}_{height}"
|
||||||
|
|
||||||
|
# 检查缓存
|
||||||
|
if cache_key in _pixmap_cache:
|
||||||
|
# 更新LRU列表
|
||||||
|
global _pixmap_cache_lru
|
||||||
|
if cache_key in _pixmap_cache_lru:
|
||||||
|
_pixmap_cache_lru.remove(cache_key)
|
||||||
|
_pixmap_cache_lru.append(cache_key)
|
||||||
|
return _pixmap_cache[cache_key]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 加载原始图片
|
||||||
|
pixmap = QtGui.QPixmap(image_path)
|
||||||
|
|
||||||
|
if not pixmap.isNull():
|
||||||
|
# 缩放图片
|
||||||
|
if width > 0 and height > 0:
|
||||||
|
pixmap = pixmap.scaled(width, height, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
|
||||||
|
|
||||||
|
# 缓存清理 - 如果超出最大缓存大小,删除最不常用的项目
|
||||||
|
global _pixmap_cache_max_size
|
||||||
|
if len(_pixmap_cache) >= _pixmap_cache_max_size and _pixmap_cache_lru:
|
||||||
|
oldest_key = _pixmap_cache_lru.pop(0)
|
||||||
|
if oldest_key in _pixmap_cache:
|
||||||
|
del _pixmap_cache[oldest_key]
|
||||||
|
|
||||||
|
# 存入缓存
|
||||||
|
_pixmap_cache[cache_key] = pixmap
|
||||||
|
_pixmap_cache_lru.append(cache_key)
|
||||||
|
return pixmap
|
||||||
|
else:
|
||||||
|
# 创建一个纯色背景作为替代
|
||||||
|
fallback_pixmap = QtGui.QPixmap(width, height)
|
||||||
|
fallback_pixmap.fill(QtGui.QColor(60, 60, 60))
|
||||||
|
return fallback_pixmap
|
||||||
|
except Exception as e:
|
||||||
|
print(f"加载图像失败 ({image_path}): {str(e)}")
|
||||||
|
fallback_pixmap = QtGui.QPixmap(width, height)
|
||||||
|
fallback_pixmap.fill(QtGui.QColor(60, 60, 60))
|
||||||
|
return fallback_pixmap
|
||||||
|
|
||||||
|
def create_dna_preview_button(dna_info, item_size, on_click_callback):
|
||||||
|
"""
|
||||||
|
创建DNA预览按钮
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dna_info (dict): DNA文件信息
|
||||||
|
item_size (tuple): 按钮尺寸 (宽, 高)
|
||||||
|
on_click_callback (function): 点击回调函数
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
QPushButton: 创建的DNA预览按钮
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 创建按钮
|
||||||
|
button = QtWidgets.QPushButton()
|
||||||
|
button.setObjectName(f"dna_button_{dna_info['name']}")
|
||||||
|
button.setFixedSize(item_size[0], item_size[1])
|
||||||
|
button.setProperty("class", "dna-preview-button")
|
||||||
|
|
||||||
|
# 创建垂直布局
|
||||||
|
layout = QtWidgets.QVBoxLayout(button)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
layout.setSpacing(2)
|
||||||
|
|
||||||
|
# 创建图像标签
|
||||||
|
image_label = QtWidgets.QLabel()
|
||||||
|
image_label.setObjectName(f"image_{dna_info['name']}")
|
||||||
|
image_label.setFixedSize(item_size[0], item_size[0]) # 正方形图像区域
|
||||||
|
image_label.setScaledContents(True)
|
||||||
|
|
||||||
|
# 使用预缓存加载图像
|
||||||
|
pixmap = get_pixmap(dna_info['image_path'], item_size[0], item_size[0])
|
||||||
|
image_label.setPixmap(pixmap)
|
||||||
|
|
||||||
|
# 创建名称标签
|
||||||
|
name_label = QtWidgets.QLabel(dna_info['name'])
|
||||||
|
name_label.setObjectName(f"name_{dna_info['name']}")
|
||||||
|
name_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
name_label.setStyleSheet("background-color: rgba(0, 0, 0, 128); padding: 2px;")
|
||||||
|
|
||||||
|
# 添加到布局
|
||||||
|
layout.addWidget(image_label)
|
||||||
|
layout.addWidget(name_label)
|
||||||
|
|
||||||
|
# 设置按钮数据
|
||||||
|
button.setProperty("dna_path", dna_info['dna_path'])
|
||||||
|
|
||||||
|
# 连接点击信号
|
||||||
|
button.clicked.connect(lambda: on_click_callback(dna_info['dna_path']))
|
||||||
|
|
||||||
|
return button
|
||||||
|
except Exception as e:
|
||||||
|
print(f"创建DNA预览按钮失败 ({dna_info.get('name', 'unknown')}): {str(e)}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
# 返回一个简单的替代按钮
|
||||||
|
fallback_button = QtWidgets.QPushButton(dna_info.get('name', 'DNA'))
|
||||||
|
fallback_button.setFixedSize(item_size[0], item_size[1])
|
||||||
|
if on_click_callback:
|
||||||
|
fallback_button.clicked.connect(lambda: on_click_callback(dna_info.get('dna_path', '')))
|
||||||
|
return fallback_button
|
||||||
|
|
||||||
|
# 缓存上一次的布局信息,避免不必要的重新计算
|
||||||
|
_last_layout_info = {
|
||||||
|
"parent_width": 0,
|
||||||
|
"item_size": (0, 0),
|
||||||
|
"spacing": 0,
|
||||||
|
"items_per_row": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
def load_dna_preview_buttons(ui_instance):
|
||||||
|
"""
|
||||||
|
为UI加载DNA预览按钮
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ui_instance: UI实例
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 检查UI实例是否有效
|
||||||
|
if not ui_instance or not hasattr(ui_instance, 'controls'):
|
||||||
|
print("无效的UI实例,无法加载DNA预览按钮")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 获取DNA文件列表
|
||||||
|
dna_files = ui_instance.dna_files
|
||||||
|
|
||||||
|
# 检查是否有DNA文件
|
||||||
|
if not dna_files:
|
||||||
|
# 如果还没有加载DNA文件,则先加载
|
||||||
|
ui_instance.dna_files = get_dna_files()
|
||||||
|
dna_files = ui_instance.dna_files
|
||||||
|
if not dna_files:
|
||||||
|
print("没有找到DNA文件,无法加载预览按钮")
|
||||||
|
if 'presets_count_label' in ui_instance.controls:
|
||||||
|
ui_instance.controls['presets_count_label'].setText("0")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 更新预设数量标签
|
||||||
|
if 'presets_count_label' in ui_instance.controls:
|
||||||
|
ui_instance.controls['presets_count_label'].setText(str(len(dna_files)))
|
||||||
|
|
||||||
|
# 获取滚动区域宽度
|
||||||
|
if 'presets_content' in ui_instance.controls:
|
||||||
|
scroll_width = ui_instance.controls['presets_content'].width()
|
||||||
|
else:
|
||||||
|
scroll_width = 400 # 默认宽度
|
||||||
|
|
||||||
|
# 更新DNA网格布局
|
||||||
|
update_dna_grid_layout(
|
||||||
|
ui_instance.controls["presets_flow_layout"],
|
||||||
|
scroll_width,
|
||||||
|
ui_instance.dna_button_size,
|
||||||
|
10, # 间距
|
||||||
|
dna_files,
|
||||||
|
lambda dna_path: on_dna_button_clicked(dna_path, ui_instance)
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
print(f"加载DNA预览按钮失败: {str(e)}")
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_dna_grid_layout(flow_layout, parent_width, item_size, spacing, dna_files, on_dna_clicked):
|
||||||
|
"""
|
||||||
|
更新DNA网格布局,根据父容器宽度动态调整每行显示的DNA项目数量
|
||||||
|
|
||||||
|
Args:
|
||||||
|
flow_layout (QGridLayout): 网格布局
|
||||||
|
parent_width (int): 父容器宽度
|
||||||
|
item_size (tuple): 项目尺寸 (宽, 高)
|
||||||
|
spacing (int): 项目间的间距
|
||||||
|
dna_files (list): DNA文件信息列表
|
||||||
|
on_dna_clicked (function): DNA按钮点击回调函数
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 检查参数有效性
|
||||||
|
if flow_layout is None:
|
||||||
|
print("错误: flow_layout 为空")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not dna_files:
|
||||||
|
print("警告: 没有DNA文件可以显示")
|
||||||
|
return
|
||||||
|
|
||||||
|
if parent_width <= 0:
|
||||||
|
print(f"警告: 父容器宽度无效 ({parent_width})")
|
||||||
|
parent_width = 400 # 使用默认宽度
|
||||||
|
|
||||||
|
global _last_layout_info
|
||||||
|
|
||||||
|
# 计算每行可容纳的项目数量
|
||||||
|
items_per_row = calculate_grid_layout(parent_width, item_size[0], spacing)
|
||||||
|
|
||||||
|
# 如果布局信息与上次相同,则不需要重新布局
|
||||||
|
if (_last_layout_info["parent_width"] == parent_width and
|
||||||
|
_last_layout_info["item_size"] == item_size and
|
||||||
|
_last_layout_info["spacing"] == spacing and
|
||||||
|
_last_layout_info["items_per_row"] == items_per_row and
|
||||||
|
flow_layout.count() > 0):
|
||||||
|
return
|
||||||
|
|
||||||
|
# 更新布局信息缓存
|
||||||
|
_last_layout_info = {
|
||||||
|
"parent_width": parent_width,
|
||||||
|
"item_size": item_size,
|
||||||
|
"spacing": spacing,
|
||||||
|
"items_per_row": items_per_row
|
||||||
|
}
|
||||||
|
|
||||||
|
print(f"布局信息: 宽度 = {parent_width}, 项目宽度 = {item_size[0]}, 每行项目数 = {items_per_row}")
|
||||||
|
|
||||||
|
# 先移除所有现有控件
|
||||||
|
for i in reversed(range(flow_layout.count())):
|
||||||
|
item = flow_layout.itemAt(i)
|
||||||
|
if item and item.widget():
|
||||||
|
item.widget().setParent(None) # 从父窗口分离
|
||||||
|
item.widget().deleteLater() # 稍后删除控件
|
||||||
|
|
||||||
|
# 重新添加DNA预览按钮
|
||||||
|
for i, dna_info in enumerate(dna_files):
|
||||||
|
try:
|
||||||
|
row = i // items_per_row
|
||||||
|
col = i % items_per_row
|
||||||
|
|
||||||
|
# 创建DNA预览按钮
|
||||||
|
dna_button = create_dna_preview_button(dna_info, item_size, on_dna_clicked)
|
||||||
|
|
||||||
|
# 添加到网格布局
|
||||||
|
flow_layout.addWidget(dna_button, row, col)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"添加DNA按钮失败 ({dna_info['name']}): {str(e)}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"更新DNA网格布局失败: {str(e)}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
# 保存上次调整大小的时间,用于限制调整频率
|
||||||
|
_last_resize_time = 0
|
||||||
|
_resize_throttle = 200 # 毫秒
|
||||||
|
|
||||||
|
def on_presets_content_resize(event, ui_instance):
|
||||||
|
"""
|
||||||
|
预设内容区域大小变化事件,限制更新频率以提高性能
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event: 调整大小事件
|
||||||
|
ui_instance: UI实例
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 检查UI实例是否有效
|
||||||
|
if not ui_instance or not hasattr(ui_instance, 'controls') or "presets_content" not in ui_instance.controls:
|
||||||
|
# 如果UI实例无效,只调用原始的resizeEvent
|
||||||
|
QtWidgets.QWidget.resizeEvent(event.widget(), event)
|
||||||
|
return
|
||||||
|
|
||||||
|
global _last_resize_time
|
||||||
|
|
||||||
|
# 调用父类的resizeEvent
|
||||||
|
QtWidgets.QWidget.resizeEvent(ui_instance.controls["presets_content"], event)
|
||||||
|
|
||||||
|
# 获取当前时间
|
||||||
|
current_time = QtCore.QTime.currentTime().msecsSinceStartOfDay()
|
||||||
|
|
||||||
|
# 如果距离上次更新不足阈值时间,则跳过更新
|
||||||
|
if current_time - _last_resize_time < _resize_throttle:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 更新上次调整时间
|
||||||
|
_last_resize_time = current_time
|
||||||
|
|
||||||
|
# 确保所有必要的控件和属性都存在
|
||||||
|
required_keys = ["presets_flow_layout"]
|
||||||
|
required_attrs = ["dna_button_size", "dna_files"]
|
||||||
|
|
||||||
|
for key in required_keys:
|
||||||
|
if key not in ui_instance.controls:
|
||||||
|
print(f"警告: '{key}' 未在UI控件中找到")
|
||||||
|
return
|
||||||
|
|
||||||
|
for attr in required_attrs:
|
||||||
|
if not hasattr(ui_instance, attr):
|
||||||
|
print(f"警告: '{attr}' 属性未在UI实例中找到")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 更新DNA网格布局
|
||||||
|
update_dna_grid_layout(
|
||||||
|
ui_instance.controls["presets_flow_layout"],
|
||||||
|
event.size().width(),
|
||||||
|
ui_instance.dna_button_size,
|
||||||
|
10, # 间距
|
||||||
|
ui_instance.dna_files,
|
||||||
|
lambda dna_path: on_dna_button_clicked(dna_path, ui_instance)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
print(f"调整DNA预览布局失败: {str(e)}")
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
def on_dna_button_clicked(dna_path, ui_instance):
|
||||||
|
"""
|
||||||
|
DNA按钮点击事件
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dna_path: DNA文件路径
|
||||||
|
ui_instance: UI实例
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 检查UI实例是否有效
|
||||||
|
if not ui_instance or not hasattr(ui_instance, 'controls'):
|
||||||
|
print("警告: UI实例无效,无法更新DNA路径")
|
||||||
|
# 仍然尝试加载DNA文件,即使无法更新UI
|
||||||
|
load_selected_dna(dna_path)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 加载选中的DNA文件
|
||||||
|
load_selected_dna(dna_path, ui_instance)
|
||||||
|
|
||||||
|
# 更新UI中的DNA路径输入框
|
||||||
|
if "presets_dna_input" in ui_instance.controls:
|
||||||
|
ui_instance.controls["presets_dna_input"].setText(dna_path)
|
||||||
|
else:
|
||||||
|
print("警告: 'presets_dna_input' 未创建")
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
print(f"点击DNA按钮处理失败: {str(e)}")
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
#------------------------------------ UI UTILITIES ------------------------------------
|
#------------------------------------ UI UTILITIES ------------------------------------
|
||||||
def browse_file(parent_widget, title, line_edit, file_type=None):
|
def browse_file(parent_widget, title, line_edit, file_type=None):
|
||||||
"""
|
"""
|
||||||
@@ -265,4 +736,14 @@ def build_rigging(*args):
|
|||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"构建绑定系统失败: {str(e)}")
|
print(f"构建绑定系统失败: {str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def clear_pixmap_cache():
|
||||||
|
"""
|
||||||
|
清除图片缓存
|
||||||
|
在不再需要缓存时调用,例如关闭窗口时
|
||||||
|
"""
|
||||||
|
global _pixmap_cache, _pixmap_cache_lru
|
||||||
|
_pixmap_cache.clear()
|
||||||
|
_pixmap_cache_lru.clear()
|
||||||
|
print("图片缓存已清除")
|
Reference in New Issue
Block a user