Update
This commit is contained in:
@@ -56,6 +56,98 @@ TOOL_HEIGHT = config.TOOL_HEIGHT
|
||||
from scripts.ui import localization
|
||||
TEXT = localization.TEXT
|
||||
|
||||
class FlowLayout(QtWidgets.QLayout):
|
||||
"""
|
||||
流式布局 - 自动调整每行的项目数量,保持固定间距
|
||||
来源: Qt文档示例改进版
|
||||
"""
|
||||
def __init__(self, parent=None, margin=0, spacing=-1):
|
||||
super(FlowLayout, self).__init__(parent)
|
||||
|
||||
if parent is not None:
|
||||
self.setContentsMargins(margin, margin, margin, margin)
|
||||
|
||||
self.setSpacing(spacing)
|
||||
|
||||
self._item_list = []
|
||||
|
||||
def __del__(self):
|
||||
item = self.takeAt(0)
|
||||
while item:
|
||||
item = self.takeAt(0)
|
||||
|
||||
def addItem(self, item):
|
||||
self._item_list.append(item)
|
||||
|
||||
def count(self):
|
||||
return len(self._item_list)
|
||||
|
||||
def itemAt(self, index):
|
||||
if 0 <= index < len(self._item_list):
|
||||
return self._item_list[index]
|
||||
|
||||
return None
|
||||
|
||||
def takeAt(self, index):
|
||||
if 0 <= index < len(self._item_list):
|
||||
return self._item_list.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._do_layout(QtCore.QRect(0, 0, width, 0), True)
|
||||
return height
|
||||
|
||||
def setGeometry(self, rect):
|
||||
super(FlowLayout, self).setGeometry(rect)
|
||||
self._do_layout(rect, False)
|
||||
|
||||
def sizeHint(self):
|
||||
return self.minimumSize()
|
||||
|
||||
def minimumSize(self):
|
||||
size = QtCore.QSize()
|
||||
|
||||
for item in self._item_list:
|
||||
size = size.expandedTo(item.minimumSize())
|
||||
|
||||
margin = self.contentsMargins()
|
||||
size += QtCore.QSize(margin.left() + margin.right(), margin.top() + margin.bottom())
|
||||
return size
|
||||
|
||||
def _do_layout(self, rect, test_only=False):
|
||||
x = rect.x()
|
||||
y = rect.y()
|
||||
line_height = 0
|
||||
spacing = self.spacing()
|
||||
|
||||
for item in self._item_list:
|
||||
widget = item.widget()
|
||||
item_width = item.sizeHint().width()
|
||||
item_height = item.sizeHint().height()
|
||||
|
||||
next_x = x + item_width + spacing
|
||||
|
||||
if next_x - spacing > rect.right() and line_height > 0:
|
||||
x = rect.x()
|
||||
y = y + line_height + spacing
|
||||
next_x = x + item_width + spacing
|
||||
line_height = 0
|
||||
|
||||
if not test_only:
|
||||
item.setGeometry(QtCore.QRect(QtCore.QPoint(x, y), item.sizeHint()))
|
||||
|
||||
x = next_x
|
||||
line_height = max(line_height, item_height)
|
||||
|
||||
return y + line_height - rect.y()
|
||||
|
||||
class RiggingUI(ui_utils.BaseUI):
|
||||
"""
|
||||
绑定系统UI类 - 负责显示骨骼绑定编辑界面和基础操作
|
||||
@@ -75,7 +167,7 @@ class RiggingUI(ui_utils.BaseUI):
|
||||
|
||||
# 初始化DNA文件列表和按钮尺寸
|
||||
self.dna_files = []
|
||||
self.dna_button_size = (120, 150)
|
||||
self.dna_button_size = (140, 170) # 增加按钮尺寸,提供更好的视觉效果
|
||||
|
||||
# 初始化UI
|
||||
self.create_widgets()
|
||||
@@ -86,7 +178,7 @@ class RiggingUI(ui_utils.BaseUI):
|
||||
self.update_language()
|
||||
|
||||
# 加载DNA文件和预览图 - 移到最后执行确保所有控件已创建
|
||||
self.load_dna_files()
|
||||
utils_rigging.load_dna_files(self)
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""
|
||||
@@ -142,11 +234,11 @@ class RiggingUI(ui_utils.BaseUI):
|
||||
self.controls["presets_content"] = QtWidgets.QWidget()
|
||||
self.controls["presets_content"].setObjectName("presetsContent")
|
||||
|
||||
# 预设流布局
|
||||
self.controls["presets_flow_layout"] = QtWidgets.QGridLayout(self.controls["presets_content"])
|
||||
# 使用流式布局替代原来的网格布局
|
||||
self.controls["presets_flow_layout"] = FlowLayout(self.controls["presets_content"])
|
||||
self.controls["presets_flow_layout"].setObjectName("presetsFlowLayout")
|
||||
self.controls["presets_flow_layout"].setContentsMargins(5, 5, 5, 5)
|
||||
self.controls["presets_flow_layout"].setSpacing(10)
|
||||
self.controls["presets_flow_layout"].setContentsMargins(10, 10, 10, 10)
|
||||
self.controls["presets_flow_layout"].setSpacing(15) # 固定间距
|
||||
|
||||
# 设置滚动区域内容
|
||||
self.controls["presets_scroll_area"].setWidget(self.controls["presets_content"])
|
||||
@@ -162,12 +254,13 @@ class RiggingUI(ui_utils.BaseUI):
|
||||
self.controls["presets_count_label"].setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.controls["presets_count_label"].setStyleSheet("font-weight: bold;")
|
||||
|
||||
# 滑块
|
||||
# 滑块 - 用于调整预览图大小
|
||||
self.controls["presets_slider"] = QtWidgets.QSlider(QtCore.Qt.Horizontal)
|
||||
self.controls["presets_slider"].setObjectName("presetsSlider")
|
||||
self.controls["presets_slider"].setMinimum(0)
|
||||
self.controls["presets_slider"].setMaximum(100)
|
||||
self.controls["presets_slider"].setValue(50)
|
||||
self.controls["presets_slider"].setMinimum(20) # 最小值
|
||||
self.controls["presets_slider"].setMaximum(100) # 最大值
|
||||
self.controls["presets_slider"].setValue(60) # 默认值
|
||||
self.controls["presets_slider"].setToolTip("调整预览图大小")
|
||||
|
||||
# 导出预设按钮
|
||||
self.buttons["export_presets"] = QtWidgets.QPushButton(TEXT("export_presets", "导出预设"))
|
||||
@@ -486,6 +579,9 @@ class RiggingUI(ui_utils.BaseUI):
|
||||
self.buttons["export_presets"].clicked.connect(utils_rigging.export_dna)
|
||||
self.buttons["import_presets"].clicked.connect(utils_rigging.import_dna)
|
||||
|
||||
# 预设滑块连接 - 控制预览按钮大小
|
||||
self.controls["presets_slider"].valueChanged.connect(lambda value: utils_rigging.on_presets_slider_changed(self, value))
|
||||
|
||||
# 浏览文件按钮连接
|
||||
self.buttons["browse_path"].clicked.connect(lambda: utils_rigging.browse_file(self, "项目路径", self.controls["project_path_input"]))
|
||||
self.buttons["browse_dna"].clicked.connect(lambda: utils_rigging.browse_file(self, "DNA文件", self.controls["presets_dna_input"], "dna"))
|
||||
@@ -493,25 +589,4 @@ class RiggingUI(ui_utils.BaseUI):
|
||||
# 底部按钮连接
|
||||
self.buttons["remove_all"].clicked.connect(utils_rigging.remove_all)
|
||||
self.buttons["import_skeleton"].clicked.connect(utils_rigging.import_skeleton)
|
||||
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()
|
||||
self.buttons["build_rigging"].clicked.connect(utils_rigging.build_rigging)
|
@@ -256,31 +256,63 @@ def create_dna_preview_button(dna_info, item_size, on_click_callback):
|
||||
button.setObjectName(f"dna_button_{dna_info['name']}")
|
||||
button.setFixedSize(item_size[0], item_size[1])
|
||||
button.setProperty("class", "dna-preview-button")
|
||||
button.setCursor(QtCore.Qt.PointingHandCursor) # 鼠标悬停时显示手型光标
|
||||
|
||||
# 创建垂直布局
|
||||
layout = QtWidgets.QVBoxLayout(button)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(2)
|
||||
|
||||
# 创建图像容器
|
||||
image_container = QtWidgets.QWidget()
|
||||
image_container.setObjectName(f"image_container_{dna_info['name']}")
|
||||
image_container.setFixedSize(item_size[0], item_size[0] - 10) # 留出底部名称空间
|
||||
image_container.setStyleSheet("background-color: #2A2A2A; border-radius: 4px;")
|
||||
|
||||
# 创建图像标签
|
||||
image_label = QtWidgets.QLabel()
|
||||
image_label = QtWidgets.QLabel(image_container)
|
||||
image_label.setObjectName(f"image_{dna_info['name']}")
|
||||
image_label.setFixedSize(item_size[0], item_size[0]) # 正方形图像区域
|
||||
image_label.setScaledContents(True)
|
||||
image_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
# 使用预缓存加载图像
|
||||
pixmap = get_pixmap(dna_info['image_path'], item_size[0], item_size[0])
|
||||
pixmap = get_pixmap(dna_info['image_path'], item_size[0] - 16, item_size[0] - 26)
|
||||
image_label.setPixmap(pixmap)
|
||||
|
||||
# 创建图像标签布局
|
||||
image_layout = QtWidgets.QVBoxLayout(image_container)
|
||||
image_layout.setContentsMargins(8, 8, 8, 8)
|
||||
image_layout.addWidget(image_label, 0, QtCore.Qt.AlignCenter)
|
||||
|
||||
# 创建名称标签
|
||||
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;")
|
||||
name_label.setStyleSheet("""
|
||||
background-color: transparent;
|
||||
color: #E0E0E0;
|
||||
font-weight: bold;
|
||||
padding: 4px;
|
||||
""")
|
||||
|
||||
# 添加到布局
|
||||
layout.addWidget(image_label)
|
||||
layout.addWidget(name_label)
|
||||
layout.addWidget(image_container, 0, QtCore.Qt.AlignCenter)
|
||||
layout.addWidget(name_label, 0, QtCore.Qt.AlignCenter)
|
||||
|
||||
# 设置按钮样式
|
||||
button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #333333;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 0px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #444444;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #555555;
|
||||
}
|
||||
""")
|
||||
|
||||
# 设置按钮数据
|
||||
button.setProperty("dna_path", dna_info['dna_path'])
|
||||
@@ -365,10 +397,10 @@ def load_dna_preview_buttons(ui_instance):
|
||||
|
||||
def update_dna_grid_layout(flow_layout, parent_width, item_size, spacing, dna_files, on_dna_clicked):
|
||||
"""
|
||||
更新DNA网格布局,根据父容器宽度动态调整每行显示的DNA项目数量
|
||||
更新DNA布局,根据父容器宽度动态调整DNA按钮布局
|
||||
|
||||
Args:
|
||||
flow_layout (QGridLayout): 网格布局
|
||||
flow_layout: 流式布局
|
||||
parent_width (int): 父容器宽度
|
||||
item_size (tuple): 项目尺寸 (宽, 高)
|
||||
spacing (int): 项目间的间距
|
||||
@@ -384,56 +416,28 @@ def update_dna_grid_layout(flow_layout, parent_width, item_size, spacing, dna_fi
|
||||
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)
|
||||
# 先清空现有控件
|
||||
while flow_layout.count() > 0:
|
||||
item = flow_layout.takeAt(0)
|
||||
if item and item.widget():
|
||||
item.widget().setParent(None) # 从父窗口分离
|
||||
item.widget().deleteLater() # 稍后删除控件
|
||||
|
||||
# 重新添加DNA预览按钮
|
||||
for i, dna_info in enumerate(dna_files):
|
||||
widget = item.widget()
|
||||
widget.setParent(None)
|
||||
widget.deleteLater()
|
||||
|
||||
# 创建并添加DNA预览按钮
|
||||
for dna_info in 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)
|
||||
# 添加到流式布局
|
||||
flow_layout.addWidget(dna_button)
|
||||
except Exception as e:
|
||||
print(f"添加DNA按钮失败 ({dna_info['name']}): {str(e)}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"更新DNA网格布局失败: {str(e)}")
|
||||
print(f"更新DNA布局失败: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
@@ -444,6 +448,7 @@ _resize_throttle = 200 # 毫秒
|
||||
def on_presets_content_resize(event, ui_instance):
|
||||
"""
|
||||
预设内容区域大小变化事件,限制更新频率以提高性能
|
||||
流式布局会自动处理大部分布局工作,这里只需更新缩放比例
|
||||
|
||||
Args:
|
||||
event: 调整大小事件
|
||||
@@ -456,6 +461,7 @@ def on_presets_content_resize(event, ui_instance):
|
||||
QtWidgets.QWidget.resizeEvent(event.widget(), event)
|
||||
return
|
||||
|
||||
# 声明全局变量
|
||||
global _last_resize_time
|
||||
|
||||
# 调用父类的resizeEvent
|
||||
@@ -471,29 +477,11 @@ def on_presets_content_resize(event, ui_instance):
|
||||
# 更新上次调整时间
|
||||
_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)
|
||||
)
|
||||
# 对于流式布局,大多数情况下不需要手动刷新
|
||||
# 但在某些情况下可能需要手动触发布局更新
|
||||
if hasattr(ui_instance.controls["presets_flow_layout"], "update"):
|
||||
ui_instance.controls["presets_flow_layout"].update()
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
print(f"调整DNA预览布局失败: {str(e)}")
|
||||
@@ -746,4 +734,89 @@ def clear_pixmap_cache():
|
||||
global _pixmap_cache, _pixmap_cache_lru
|
||||
_pixmap_cache.clear()
|
||||
_pixmap_cache_lru.clear()
|
||||
print("图片缓存已清除")
|
||||
print("图片缓存已清除")
|
||||
|
||||
#------------------------------------ UI 预览调整函数 ------------------------------------
|
||||
def on_presets_slider_changed(ui_instance, value):
|
||||
"""
|
||||
预设滑块值变化处理 - 调整预览按钮大小
|
||||
|
||||
Args:
|
||||
ui_instance: UI实例
|
||||
value: 滑块当前值
|
||||
"""
|
||||
try:
|
||||
# 检查UI实例是否有效
|
||||
if not ui_instance or not hasattr(ui_instance, 'controls'):
|
||||
print("警告: UI实例无效,无法调整预览按钮大小")
|
||||
return
|
||||
|
||||
# 计算新的按钮尺寸 (基于滑块值进行缩放)
|
||||
# 范围从最小100x120到最大180x220
|
||||
base_width = 140 # 基础宽度
|
||||
base_height = 170 # 基础高度
|
||||
|
||||
# 获取滑块范围
|
||||
min_val = ui_instance.controls["presets_slider"].minimum()
|
||||
max_val = ui_instance.controls["presets_slider"].maximum()
|
||||
|
||||
# 避免除以零
|
||||
scale_range = max(1, max_val - min_val)
|
||||
|
||||
# 计算缩放比例 (0.7 到 1.3)
|
||||
scale_factor = 0.7 + 0.6 * ((value - min_val) / scale_range)
|
||||
|
||||
# 应用缩放
|
||||
new_width = int(base_width * scale_factor)
|
||||
new_height = int(base_height * scale_factor)
|
||||
|
||||
# 确保尺寸在合理范围内
|
||||
new_width = max(100, min(180, new_width))
|
||||
new_height = max(120, min(220, new_height))
|
||||
|
||||
# 更新按钮尺寸
|
||||
old_size = ui_instance.dna_button_size
|
||||
ui_instance.dna_button_size = (new_width, new_height)
|
||||
|
||||
# 如果尺寸有明显变化,重新加载DNA预览按钮
|
||||
if abs(old_size[0] - new_width) > 10 or abs(old_size[1] - new_height) > 10:
|
||||
# 重新加载预览按钮
|
||||
load_dna_preview_buttons(ui_instance)
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
print(f"调整DNA预览按钮大小失败: {str(e)}")
|
||||
traceback.print_exc()
|
||||
|
||||
def load_dna_files(ui_instance):
|
||||
"""
|
||||
加载DNA文件和预览
|
||||
在所有UI控件创建完成后执行
|
||||
|
||||
Args:
|
||||
ui_instance: UI实例
|
||||
"""
|
||||
try:
|
||||
# 检查UI实例是否有效
|
||||
if not ui_instance or not hasattr(ui_instance, 'controls'):
|
||||
print("警告: UI实例无效,无法加载DNA文件")
|
||||
return
|
||||
|
||||
# 加载DNA文件和预览图
|
||||
ui_instance.dna_files = get_dna_files()
|
||||
|
||||
# 更新预设数量标签
|
||||
if 'presets_count_label' in ui_instance.controls:
|
||||
ui_instance.controls['presets_count_label'].setText(str(len(ui_instance.dna_files)))
|
||||
|
||||
# 调用加载预览按钮函数
|
||||
load_dna_preview_buttons(ui_instance)
|
||||
|
||||
# 自动选中第一个DNA文件 (如果存在)
|
||||
if ui_instance.dna_files and 'presets_dna_input' in ui_instance.controls:
|
||||
ui_instance.controls['presets_dna_input'].setText(ui_instance.dna_files[0]['dna_path'])
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
print(f"加载DNA文件失败: {str(e)}")
|
||||
traceback.print_exc()
|
Reference in New Issue
Block a user