-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathpanel_manager.py
More file actions
602 lines (495 loc) · 27.3 KB
/
panel_manager.py
File metadata and controls
602 lines (495 loc) · 27.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
"""
面板管理器模块 - 负责创建和管理工具面板
"""
from PyQt5.QtWidgets import (
QDockWidget, QVBoxLayout, QHBoxLayout, QWidget, QLabel,
QSlider, QGroupBox, QCheckBox, QPushButton, QComboBox, QScrollArea
)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from localization import translator
from functools import partial
class PanelManager:
"""工具面板管理器 - 负责创建和管理工具面板"""
def __init__(self, main_window):
"""
初始化面板管理器
Args:
main_window: 主窗口实例
"""
self.main_window = main_window
self.dock_widgets = {}
self.controls = {}
def create_parameter_panel(self):
"""创建参数面板"""
param_dock = QDockWidget('', self.main_window) # 移除标题文字
param_dock.setFeatures(QDockWidget.NoDockWidgetFeatures) # 禁止折叠和移动
# 隐藏标题栏但保留dock功能 - 设置一个空的widget作为标题栏
empty_title_bar = QWidget()
empty_title_bar.setFixedHeight(0) # 设置高度为0,完全隐藏
param_dock.setTitleBarWidget(empty_title_bar)
# 设置dock的宽度限制,确保不会被压缩
param_dock.setMinimumWidth(280)
param_dock.setMaximumWidth(320)
# 创建滚动区域
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True) # 允许内容自动调整大小
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # 禁用水平滚动条
scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) # 需要时显示垂直滚动条
scroll_area.setFrameStyle(0) # 移除边框,使其更美观
# 滚动条样式现在统一在 style.qss 中管理
# 设置滚动区域的宽度限制,防止被压缩
scroll_area.setMinimumWidth(280) # 设置最小宽度
scroll_area.setMaximumWidth(320) # 设置最大宽度,防止过宽
# 创建内容widget
param_widget = QWidget()
layout = QVBoxLayout()
layout.setContentsMargins(10, 10, 10, 10)
layout.setSpacing(12)
# 创建各个组件
self._create_brush_group(layout)
self._create_mode_group(layout)
self._create_flow_group(layout)
self._create_uv_overlay_group(layout)
self._create_overlay_group(layout)
self._create_fill_controls(layout)
layout.addStretch() # 添加伸缩空间,使其他组件靠上
self._create_shortcut_group(layout)
param_widget.setLayout(layout)
# 将内容widget放入滚动区域
scroll_area.setWidget(param_widget)
# 将滚动区域设置为dock的widget
param_dock.setWidget(scroll_area)
self.main_window.addDockWidget(Qt.LeftDockWidgetArea, param_dock)
self.dock_widgets["parameter_panel"] = param_dock
return param_dock
def _create_brush_group(self, parent_layout):
"""创建笔刷参数组"""
brush_group = QGroupBox(translator.tr("brush_parameters"))
brush_layout = QVBoxLayout()
brush_layout.setSpacing(8)
# 笔刷大小参数
brush_size_label = QLabel(f"{translator.tr('brush_size')}: 40")
font = brush_size_label.font()
font_size = font.pointSize()
font.setPointSize(int(font_size * 1.2))
brush_size_label.setFont(font)
brush_size_slider = QSlider(Qt.Horizontal)
brush_size_slider.setMinimum(5)
brush_size_slider.setMaximum(200)
brush_size_slider.setValue(40) # 默认值与 FlowmapCanvas 中的相同
# 流动强度参数
flow_strength_label = QLabel(f"{translator.tr('flow_strength')}: 0.5")
flow_strength_label.setFont(font) # 使用相同的放大字体
flow_strength_slider = QSlider(Qt.Horizontal)
flow_strength_slider.setMinimum(1)
flow_strength_slider.setMaximum(100)
flow_strength_slider.setValue(50) # 0.5 * 100
# 速度灵敏度参数
speed_sensitivity_label = QLabel(f"{translator.tr('speed_sensitivity')}: 0.7")
speed_sensitivity_label.setFont(font) # 使用相同的放大字体
speed_sensitivity_slider = QSlider(Qt.Horizontal)
speed_sensitivity_slider.setMinimum(1)
speed_sensitivity_slider.setMaximum(100)
speed_sensitivity_slider.setValue(70) # 0.7 * 100
# 连接信号:拖动时仅预览;释放时才入撤销栈
brush_size_slider.sliderPressed.connect(self.main_window.on_brush_size_pressed)
brush_size_slider.valueChanged.connect(self.main_window.on_brush_size_changed)
brush_size_slider.sliderReleased.connect(self.main_window.on_brush_size_released)
flow_strength_slider.sliderPressed.connect(self.main_window.on_flow_strength_pressed)
flow_strength_slider.valueChanged.connect(self.main_window.on_flow_strength_changed)
flow_strength_slider.sliderReleased.connect(self.main_window.on_flow_strength_released)
speed_sensitivity_slider.sliderPressed.connect(lambda: self.main_window._old_param_values.__setitem__("speed_sensitivity", self.main_window.param_registry.read("speed_sensitivity") if self.main_window.param_registry.has_key("speed_sensitivity") else self.main_window.canvas_widget.speed_sensitivity))
speed_sensitivity_slider.valueChanged.connect(self.main_window.on_speed_sensitivity_changed)
speed_sensitivity_slider.sliderReleased.connect(lambda: self.main_window._on_speed_sensitivity_released_internal(speed_sensitivity_slider.value()))
# 添加笔刷参数到布局
brush_layout.addWidget(brush_size_label)
brush_layout.addWidget(brush_size_slider)
brush_layout.addWidget(flow_strength_label)
brush_layout.addWidget(flow_strength_slider)
brush_layout.addWidget(speed_sensitivity_label)
brush_layout.addWidget(speed_sensitivity_slider)
brush_group.setLayout(brush_layout)
# 存储控件引用
self.controls["brush_size_label"] = brush_size_label
self.controls["brush_size_slider"] = brush_size_slider
self.controls["flow_strength_label"] = flow_strength_label
self.controls["flow_strength_slider"] = flow_strength_slider
self.controls["speed_sensitivity_label"] = speed_sensitivity_label
self.controls["speed_sensitivity_slider"] = speed_sensitivity_slider
parent_layout.addWidget(brush_group)
def _create_mode_group(self, parent_layout):
"""创建模式设置组"""
mode_group = QGroupBox(translator.tr("mode_settings"))
mode_layout = QVBoxLayout()
mode_layout.setSpacing(8)
# 字体
font = QFont()
font_size = font.pointSize()
font.setPointSize(int(font_size * 1.2))
# 四方连续贴图选项
seamless_checkbox = QCheckBox(translator.tr("enable_seamless"))
seamless_checkbox.setFont(font)
seamless_checkbox.setChecked(False)
seamless_checkbox.stateChanged.connect(lambda state: self._on_bool_param_changed("seamless_mode", state == Qt.Checked))
# 预览重复选项
preview_repeat_checkbox = QCheckBox(translator.tr("enable_preview_repeat"))
preview_repeat_checkbox.setFont(font)
preview_repeat_checkbox.setChecked(False)
preview_repeat_checkbox.stateChanged.connect(lambda state: self._on_bool_param_changed("preview_repeat", state == Qt.Checked))
# 添加到模式设置布局
mode_layout.addWidget(seamless_checkbox)
mode_layout.addWidget(preview_repeat_checkbox)
mode_group.setLayout(mode_layout)
# 存储控件引用
self.controls["seamless_checkbox"] = seamless_checkbox
self.controls["preview_repeat_checkbox"] = preview_repeat_checkbox
parent_layout.addWidget(mode_group)
def _create_flow_group(self, parent_layout):
"""创建流动效果组"""
flow_group = QGroupBox(translator.tr("flow_effect_control"))
flow_layout = QVBoxLayout()
flow_layout.setSpacing(8)
# 字体
font = QFont()
font_size = font.pointSize()
font.setPointSize(int(font_size * 1.2))
# 流动速度控制
flow_speed_label = QLabel(f"{translator.tr('flow_speed')}: {self.main_window.canvas_widget.flow_speed:.2f}")
flow_speed_label.setFont(font)
flow_speed_slider = QSlider(Qt.Horizontal)
flow_speed_slider.setMinimum(1)
flow_speed_slider.setMaximum(200)
flow_speed_slider.setValue(int(self.main_window.canvas_widget.flow_speed * 100))
flow_speed_slider.sliderPressed.connect(lambda: self.main_window._old_param_values.__setitem__("flow_speed", self.main_window.param_registry.read("flow_speed") if self.main_window.param_registry.has_key("flow_speed") else self.main_window.canvas_widget.flow_speed))
flow_speed_slider.valueChanged.connect(self.main_window.on_flow_speed_changed)
flow_speed_slider.sliderReleased.connect(lambda: self.main_window.on_flow_speed_changed(flow_speed_slider.value(), True) if hasattr(self.main_window, 'on_flow_speed_changed') else None)
# 流动距离控制
flow_distortion_label = QLabel(f"{translator.tr('flow_distance')}: {self.main_window.canvas_widget.flow_distortion:.2f}")
flow_distortion_label.setFont(font)
flow_distortion_slider = QSlider(Qt.Horizontal)
flow_distortion_slider.setMinimum(1)
flow_distortion_slider.setMaximum(100)
flow_distortion_slider.setValue(int(self.main_window.canvas_widget.flow_distortion * 100))
flow_distortion_slider.sliderPressed.connect(lambda: self.main_window._old_param_values.__setitem__("flow_distortion", self.main_window.param_registry.read("flow_distortion") if self.main_window.param_registry.has_key("flow_distortion") else self.main_window.canvas_widget.flow_distortion))
flow_distortion_slider.valueChanged.connect(self.main_window.on_flow_distortion_changed)
flow_distortion_slider.sliderReleased.connect(lambda: self.main_window.on_flow_distortion_changed(flow_distortion_slider.value(), True) if hasattr(self.main_window, 'on_flow_distortion_changed') else None)
# 底图缩放控制 (0.5 ~ 4.0)
base_scale = getattr(self.main_window.canvas_widget, 'base_scale', 1.0)
base_scale_label = QLabel(f"{translator.tr('base_scale')}: {float(base_scale):.2f}")
base_scale_label.setFont(font)
base_scale_slider = QSlider(Qt.Horizontal)
base_scale_slider.setMinimum(10) # 0.50
base_scale_slider.setMaximum(200) # 4.00
base_scale_slider.setValue(int(round(float(base_scale) * 100)))
def on_base_scale_changed(v:int):
val = float(v) / 100.0
base_scale_label.setText(f"{translator.tr('base_scale')}: {val:.2f}")
# 实时应用(transient),松手时入撤销栈
try:
self.main_window.param_registry.apply("base_scale", val, transient=True)
self.main_window.canvas_widget.update()
except Exception:
pass
base_scale_slider.sliderPressed.connect(lambda: self._record_old_value("base_scale"))
base_scale_slider.valueChanged.connect(on_base_scale_changed)
base_scale_slider.sliderReleased.connect(lambda: self._commit_param_change("base_scale", base_scale_slider.value() / 100.0))
# 添加到流动效果布局
flow_layout.addWidget(flow_speed_label)
flow_layout.addWidget(flow_speed_slider)
flow_layout.addWidget(flow_distortion_label)
flow_layout.addWidget(flow_distortion_slider)
flow_layout.addWidget(base_scale_label)
flow_layout.addWidget(base_scale_slider)
flow_group.setLayout(flow_layout)
# 存储控件引用
self.controls["flow_speed_label"] = flow_speed_label
self.controls["flow_speed_slider"] = flow_speed_slider
self.controls["flow_distortion_label"] = flow_distortion_label
self.controls["flow_distortion_slider"] = flow_distortion_slider
self.controls["base_scale_label"] = base_scale_label
self.controls["base_scale_slider"] = base_scale_slider
parent_layout.addWidget(flow_group)
def _create_uv_overlay_group(self, parent_layout):
"""创建UV覆盖设置组(仅3D打开时显示)"""
from localization import translator
uv_group = QGroupBox(translator.tr("uv_overlay"))
layout = QVBoxLayout()
layout.setSpacing(8)
# UV Set Selection
uv_set_label = QLabel(f"{translator.tr('uv_set')}: UV0")
uv_set_combo = QComboBox()
uv_set_combo.addItem("UV0") # Default item
def on_uv_set_changed(index):
if index >= 0:
uv_set_name = uv_set_combo.itemText(index)
uv_set_label.setText(f"{translator.tr('uv_set')}: {uv_set_name}")
# Apply UV set change through registry
try:
if hasattr(self.main_window, 'param_registry') and self.main_window.param_registry:
self.main_window.param_registry.apply("selected_uv_set", index, transient=False)
# Update 3D viewport and 2D UV overlay
self._update_uv_set_selection(index)
except Exception as e:
print(f"UV set change error: {e}")
uv_set_combo.currentIndexChanged.connect(on_uv_set_changed)
layout.addWidget(uv_set_label)
layout.addWidget(uv_set_combo)
# Opacity
opacity_label = QLabel(f"{translator.tr('uv_opacity')}: 0.70")
opacity_slider = QSlider(Qt.Horizontal)
opacity_slider.setMinimum(0)
opacity_slider.setMaximum(100)
opacity_slider.setValue(int(round(float(getattr(self.main_window.canvas_widget, 'uv_wire_opacity', 0.7)) * 100)))
def on_opacity_changed(value):
opacity = value / 100.0
opacity_label.setText(f"{translator.tr('uv_opacity')}: {opacity:.2f}")
# 通过注册表应用,支持撤销
try:
if hasattr(self.main_window, 'param_registry') and self.main_window.param_registry:
self.main_window.param_registry.apply("uv_wire_opacity", opacity, transient=True)
else:
self.main_window.canvas_widget.uv_wire_opacity = float(opacity)
self.main_window.canvas_widget.update()
except Exception:
pass
opacity_slider.sliderPressed.connect(lambda: self._record_old_value("uv_wire_opacity"))
opacity_slider.valueChanged.connect(on_opacity_changed)
opacity_slider.sliderReleased.connect(lambda: self._commit_param_change("uv_wire_opacity", opacity_slider.value() / 100.0))
# Line width
width_label = QLabel(f"{translator.tr('uv_line_width')}: 1.00")
width_slider = QSlider(Qt.Horizontal)
width_slider.setMinimum(2) # 1.0 -> 2*0.5
width_slider.setMaximum(10) # 5.0 -> 10*0.5
# map current to scaled integer
current_lw = float(getattr(self.main_window.canvas_widget, 'uv_wire_line_width', 1.0))
width_slider.setValue(int(round(current_lw / 0.5)))
def on_width_changed(v):
lw = float(v) * 0.5
width_label.setText(f"{translator.tr('uv_line_width')}: {lw:.2f}")
try:
if hasattr(self.main_window, 'param_registry') and self.main_window.param_registry:
self.main_window.param_registry.apply("uv_wire_line_width", lw, transient=True)
else:
self.main_window.canvas_widget.uv_wire_line_width = lw
self.main_window.canvas_widget.update()
except Exception:
pass
width_slider.sliderPressed.connect(lambda: self._record_old_value("uv_wire_line_width"))
width_slider.valueChanged.connect(on_width_changed)
width_slider.sliderReleased.connect(lambda: self._commit_param_change("uv_wire_line_width", float(width_slider.value()) * 0.5))
layout.addWidget(opacity_label)
layout.addWidget(opacity_slider)
layout.addWidget(width_label)
layout.addWidget(width_slider)
uv_group.setLayout(layout)
parent_layout.addWidget(uv_group)
# 记录控件并默认隐藏(由MainWindow在3D开关时显示/隐藏)
self.controls["uv_group"] = uv_group
self.controls["uv_set_label"] = uv_set_label
self.controls["uv_set_combo"] = uv_set_combo
self.controls["uv_opacity_label"] = opacity_label
self.controls["uv_opacity_slider"] = opacity_slider
self.controls["uv_width_label"] = width_label
self.controls["uv_width_slider"] = width_slider
uv_group.setVisible(False)
def _record_old_value(self, key):
try:
old = getattr(self.main_window.canvas_widget, key)
self.main_window._old_param_values[key] = old
except Exception:
pass
def _commit_param_change(self, key, new_value):
try:
from commands import ParameterChangeCommand
old_value = self.main_window._old_param_values.get(key, new_value)
if hasattr(self.main_window, 'param_registry') and self.main_window.param_registry and self.main_window.param_registry.has_key(key):
if old_value != new_value:
cmd = ParameterChangeCommand(self.main_window.param_registry, key, old_value, new_value)
self.main_window.command_mgr.execute_command(cmd)
else:
# fallback:直接赋值
setattr(self.main_window.canvas_widget, key, new_value)
except Exception as e:
print(f"commit_param_change error: {e}")
def _create_overlay_group(self, parent_layout):
"""创建参考贴图设置组"""
overlay_group = QGroupBox(translator.tr("overlay_settings"))
layout = QVBoxLayout()
layout.setSpacing(8)
font = QFont()
font_size = font.pointSize()
font.setPointSize(int(font_size * 1.2))
opacity_label = QLabel(f"{translator.tr('overlay_opacity')}: 0.5")
opacity_label.setFont(font)
opacity_slider = QSlider(Qt.Horizontal)
opacity_slider.setMinimum(0)
opacity_slider.setMaximum(100)
opacity_slider.setValue(50)
def on_opacity_changed(value):
opacity = value / 100.0
opacity_label.setText(f"{translator.tr('overlay_opacity')}: {opacity:.2f}")
# 预览实时更新,但不入栈
self.main_window.param_registry.apply("overlay_opacity", opacity, transient=True)
opacity_slider.sliderPressed.connect(lambda: self._record_overlay_old_opacity())
opacity_slider.valueChanged.connect(on_opacity_changed)
opacity_slider.sliderReleased.connect(lambda: self._commit_overlay_opacity(opacity_slider.value()))
layout.addWidget(opacity_label)
layout.addWidget(opacity_slider)
overlay_group.setLayout(layout)
self.controls["overlay_opacity_label"] = opacity_label
self.controls["overlay_opacity_slider"] = opacity_slider
self.controls["overlay_group"] = overlay_group
parent_layout.addWidget(overlay_group)
# 默认在未导入参考贴图之前隐藏整个组
overlay_group.setVisible(False)
def _on_bool_param_changed(self, key, checked):
# 使用注册表写入并入栈
try:
old = self.main_window.param_registry.read(key)
except Exception:
from app_settings import app_settings
old = getattr(app_settings, 'seamless_mode' if key == 'seamless_mode' else 'preview_repeat')
self.main_window.param_registry.apply(key, bool(checked), transient=True)
if bool(old) != bool(checked) and self.main_window.param_registry.has_key(key):
from commands import ParameterChangeCommand
cmd = ParameterChangeCommand(self.main_window.param_registry, key, bool(old), bool(checked))
self.main_window.command_mgr.execute_command(cmd)
def _create_fill_controls(self, parent_layout):
"""创建填充画布控件"""
fill_layout = QHBoxLayout()
fill_btn = QPushButton(translator.tr("fill_canvas"))
fill_btn.clicked.connect(self.main_window.fill_canvas)
color_btn = QPushButton()
color_btn.setFixedSize(32, 32)
color_btn.setStyleSheet("background-color: rgba(128, 128, 0, 255); border-radius: 4px;")
color_btn.clicked.connect(self.main_window.choose_fill_color)
fill_layout.addWidget(fill_btn)
fill_layout.addWidget(color_btn)
# 存储控件引用
self.controls["fill_button"] = fill_btn
self.controls["color_button"] = color_btn
parent_layout.addLayout(fill_layout)
def _create_shortcut_group(self, parent_layout):
"""创建快捷键信息组"""
self.shortcut_group = QGroupBox(translator.tr("shortcuts_2d"))
self.shortcut_layout = QVBoxLayout()
# 创建2D快捷键标签
self.shortcuts_2d_text = [
translator.tr("shortcut_left_click"),
translator.tr("shortcut_right_click"),
translator.tr("shortcut_middle_drag"),
translator.tr("shortcut_wheel"),
translator.tr("shortcut_space_f"),
translator.tr("shortcut_s_drag")
]
# 创建3D快捷键标签
self.shortcuts_3d_text = [
translator.tr("shortcut_left_click"),
translator.tr("shortcut_right_click"),
translator.tr("shortcut_middle_drag"),
translator.tr("shortcut_wheel"),
translator.tr("shortcut_space_f"),
translator.tr("shortcut_s_drag"),
translator.tr("shortcut_alt_rotate")
]
self.shortcut_labels = []
# 设置layout到group,然后再更新显示
self.shortcut_group.setLayout(self.shortcut_layout)
# 默认显示2D快捷键
self._update_shortcut_display(False)
# 存储控件引用
self.controls["shortcut_labels"] = self.shortcut_labels
self.controls["shortcut_group"] = self.shortcut_group
parent_layout.addWidget(self.shortcut_group)
def _update_shortcut_display(self, is_3d_mode):
"""更新快捷键显示,根据2D/3D模式切换"""
# 清除现有标签
for label in self.shortcut_labels:
label.deleteLater()
self.shortcut_labels.clear()
# 更新组标题
if is_3d_mode:
self.shortcut_group.setTitle(translator.tr("shortcuts_3d"))
shortcuts_text = self.shortcuts_3d_text
else:
self.shortcut_group.setTitle(translator.tr("shortcuts_2d"))
shortcuts_text = self.shortcuts_2d_text
# 创建新的快捷键标签
for text in shortcuts_text:
label = QLabel(text)
label.setStyleSheet("color: #999999; font-size: 11px;")
self.shortcut_labels.append(label)
self.shortcut_layout.addWidget(label)
# 更新控件引用
self.controls["shortcut_labels"] = self.shortcut_labels
def get_control(self, name):
"""获取指定名称的控件"""
return self.controls.get(name)
def _record_overlay_old_opacity(self):
try:
self.main_window._old_param_values["overlay_opacity"] = self.main_window.param_registry.read("overlay_opacity")
except Exception:
self.main_window._old_param_values["overlay_opacity"] = float(self.main_window.canvas_widget.overlay_opacity)
def _commit_overlay_opacity(self, slider_value):
new_value = slider_value / 100.0
old_value = self.main_window._old_param_values.get("overlay_opacity", new_value)
if abs(old_value - new_value) > 1e-6 and self.main_window.param_registry.has_key("overlay_opacity"):
from commands import ParameterChangeCommand
cmd = ParameterChangeCommand(self.main_window.param_registry, "overlay_opacity", old_value, new_value)
self.main_window.command_mgr.execute_command(cmd)
def update_brush_size_label(self, value):
"""更新笔刷大小标签"""
if "brush_size_label" in self.controls:
self.controls["brush_size_label"].setText(f"{translator.tr('brush_size')}: {value}")
def update_flow_strength_label(self, value):
"""更新流动强度标签"""
if "flow_strength_label" in self.controls:
self.controls["flow_strength_label"].setText(f"{translator.tr('flow_strength')}: {value:.2f}")
def update_speed_sensitivity_label(self, value):
"""更新速度灵敏度标签"""
if "speed_sensitivity_label" in self.controls:
self.controls["speed_sensitivity_label"].setText(f"{translator.tr('speed_sensitivity')}: {value:.2f}")
def update_flow_speed_label(self, value):
"""更新流动速度标签"""
if "flow_speed_label" in self.controls:
self.controls["flow_speed_label"].setText(f"{translator.tr('flow_speed')}: {value:.2f}")
def update_flow_distortion_label(self, value):
"""更新流动距离标签"""
if "flow_distortion_label" in self.controls:
self.controls["flow_distortion_label"].setText(f"{translator.tr('flow_distance')}: {value:.2f}")
def get_shortcut_labels(self):
"""获取快捷键标签列表"""
return self.controls.get("shortcut_labels", [])
def update_uv_sets(self, uv_set_names):
"""更新UV集选择下拉框"""
try:
combo = self.controls.get("uv_set_combo")
if combo and uv_set_names:
combo.blockSignals(True)
combo.clear()
for name in uv_set_names:
combo.addItem(name)
combo.setCurrentIndex(0) # 默认选择第一个UV集
combo.blockSignals(False)
# 更新标签
label = self.controls.get("uv_set_label")
if label and uv_set_names:
from localization import translator
label.setText(f"{translator.tr('uv_set')}: {uv_set_names[0]}")
except Exception as e:
print(f"update_uv_sets error: {e}")
def _update_uv_set_selection(self, uv_set_index):
"""更新UV集选择,通知3D视口和2D覆盖"""
try:
# 通知3D视口切换UV集
if hasattr(self.main_window, '_three_d_widget') and self.main_window._three_d_widget:
self.main_window._three_d_widget.set_active_uv_set(uv_set_index)
# 更新2D UV覆盖显示
if hasattr(self.main_window, '_three_d_widget') and self.main_window._three_d_widget:
uvs, indices = self.main_window._three_d_widget.get_uv_wire_data(uv_set_index)
if uvs is not None and indices is not None:
self.main_window.canvas_widget.set_uv_overlay_data(uvs, indices)
self.main_window.canvas_widget.update()
except Exception as e:
print(f"_update_uv_set_selection error: {e}")