Update
This commit is contained in:
@@ -0,0 +1,949 @@
|
||||
"""
|
||||
Curve control window
|
||||
---
|
||||
GS CurveTools License:
|
||||
This collection of code named GS CurveTools is a property of George Sladkovsky (Yehor Sladkovskyi)
|
||||
and can not be copied or distributed without his written permission.
|
||||
|
||||
GS CurveTools v1.3.10 Personal Edition
|
||||
Copyright 2025, George Sladkovsky (Yehor Sladkovskyi)
|
||||
All Rights Reserved
|
||||
|
||||
UI font is Roboto that is licensed under the Apache 2.0 License:
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Autodesk Maya is a property of Autodesk, Inc:
|
||||
https://www.autodesk.com/
|
||||
|
||||
Social Media and Contact Links:
|
||||
|
||||
Discord Server: https://discord.gg/f4DH6HQ
|
||||
Online Store: https://sladkovsky3d.artstation.com/store
|
||||
Online Documentation: https://gs-curvetools.readthedocs.io/
|
||||
Twitch Channel: https://www.twitch.tv/videonomad
|
||||
YouTube Channel: https://www.youtube.com/c/GeorgeSladkovsky
|
||||
ArtStation Portfolio: https://www.artstation.com/sladkovsky3d
|
||||
Contact Email: george.sladkovsky@gmail.com
|
||||
"""
|
||||
from functools import partial
|
||||
import maya.cmds as mc
|
||||
from gs_curvetools.api.maya_tools import deferred, no_undo, undo
|
||||
from gs_curvetools.api.qt_compat import *
|
||||
from gs_curvetools.api.utils import open_link
|
||||
from gs_curvetools.config.constants import *
|
||||
from gs_curvetools.config.folders import GetFolder
|
||||
from gs_curvetools.core.core import Core
|
||||
from gs_curvetools.debug.logger import logger
|
||||
from gs_curvetools.managers.options_manager import OptionsManager
|
||||
from gs_curvetools.managers.script_jobs import ScriptJobs
|
||||
from gs_curvetools.managers.widget_manager import WidgetManager
|
||||
from gs_curvetools.ui import style
|
||||
from gs_curvetools.ui.tooltips import Tooltips
|
||||
from gs_curvetools.ui.utils import maya_dockable_window
|
||||
from gs_curvetools.ui.widgets import Button, ColorPicker, Column, ControlSlider, FallOffCurve, FloatField, Frame, IconCheckButton, Label, LayerSelector, LineEdit, PopOutWindow, Row, maya_slider, separator
|
||||
|
||||
class CurveControlWindow(QWidget):
|
||||
"""曲线控制用户界面"""
|
||||
update_curve_control_ui = Signal()
|
||||
update_main_ui = Signal()
|
||||
|
||||
def __init__(self, parent):
|
||||
super(CurveControlWindow, self).__init__(parent)
|
||||
self.core = Core.singleton()
|
||||
self.widget_manager = WidgetManager()
|
||||
self.options_manager = OptionsManager()
|
||||
self.script_jobs = ScriptJobs.singleton()
|
||||
self.tooltips = Tooltips()
|
||||
|
||||
def open_ui(self):
|
||||
"""创建曲线控制工作区"""
|
||||
if mc.workspaceControl(WINDOWS.CurveControl.name, q=1, ex=1):
|
||||
if not mc.workspaceControl(WINDOWS.CurveControl.name, q=1, vis=1):
|
||||
mc.workspaceControl(WINDOWS.CurveControl.name, e=1, rs=1)
|
||||
deferred(self.update_main_ui.emit)()
|
||||
else:
|
||||
mc.workspaceControl(WINDOWS.CurveControl.name, e=1, vis=0)
|
||||
return
|
||||
else:
|
||||
dockable_window = maya_dockable_window(name=WINDOWS.CurveControl.name, label="曲线控制", i_w=350, i_h=750, width_property='free')
|
||||
layout = dockable_window.layout()
|
||||
assert layout is not None, '未找到布局'
|
||||
layout.addWidget(self)
|
||||
self.create_ui()
|
||||
self.update_curve_control_ui.emit()
|
||||
self.script_jobs.check_script_jobs(WINDOWS.CurveControl.name)
|
||||
|
||||
def create_ui(self):
|
||||
"""创建曲线控制用户界面"""
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(*style.scale([2, 0, 2, 0]))
|
||||
scroll_widget = QtWidgets.QWidget()
|
||||
layout = QtWidgets.QVBoxLayout(scroll_widget)
|
||||
scroll_area = QtWidgets.QScrollArea()
|
||||
scroll_area.setWidget(scroll_widget)
|
||||
main_layout.addWidget(scroll_area)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(style.scale(2))
|
||||
layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
|
||||
scroll_area.setFrameShape(QtWidgets.QFrame.Shape.NoFrame)
|
||||
scroll_area.setWidgetResizable(True)
|
||||
layout.addWidget(separator())
|
||||
with Row(layout, 'gsCurveControlHeader') as row:
|
||||
row_layout = row.layout()
|
||||
if not row_layout:
|
||||
raise RuntimeError('未找到行布局')
|
||||
layer_selector = LayerSelector('gsLayerSelector', row_layout)
|
||||
layer_selector.setFixedWidth(style.scale(40))
|
||||
layer_selector.currentIndexChanged.connect(self.core.layer_manager.change_layer_via_option_menu)
|
||||
layer_color_picker = ColorPicker('gsColorPicker', row_layout)
|
||||
layer_color_picker.setContentsMargins(*style.scale([3, 3, 1, 3]))
|
||||
layer_color_picker.setFixedWidth(style.scale(20))
|
||||
layer_color_picker.connect_command(self.core.color_mode.change_layer_color_via_picker)
|
||||
curve_color_picker = ColorPicker('gsCurveColorPicker', row_layout)
|
||||
curve_color_picker.setContentsMargins(*style.scale([1, 3, 2, 3]))
|
||||
curve_color_picker.setFixedWidth(style.scale(20))
|
||||
curve_color_picker.connect_command(self.core.color_mode.change_curve_color_via_picker)
|
||||
selected_object_name = LineEdit('selectedObjectName', row_layout)
|
||||
selected_object_name.setPlaceholderText('选择曲线')
|
||||
selected_object_name.returnPressed.connect(undo(self.core.functions.rename_selected))
|
||||
line_thickness = FloatField('lineWidth', row_layout)
|
||||
line_thickness.setFixedWidth(style.scale(35))
|
||||
line_thickness.set_range(-1, 10)
|
||||
line_thickness.set_value(-1)
|
||||
line_thickness.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, line_thickness))
|
||||
line_thickness.set_release_command(self.core.sliders.release)
|
||||
layout.addWidget(separator())
|
||||
label = Label(layout, 'selectCurvesPrompt')
|
||||
label.set_label('选择兼容的曲线')
|
||||
label.set_font_size(14)
|
||||
label.setVisible(False)
|
||||
with Column(layout, 'axisFrame') as column:
|
||||
column_layout = column.layout()
|
||||
if not column_layout:
|
||||
raise RuntimeError('未找到列布局')
|
||||
label = Label(column_layout)
|
||||
label.set_label('轴控制')
|
||||
label.set_font_size(14)
|
||||
with Row(column.layout()) as row:
|
||||
axis_button_grp = QtWidgets.QButtonGroup(row)
|
||||
self.widget_manager.add('Axis', axis_button_grp)
|
||||
axis_auto = Button(row.layout(), 'gsBindAxisAuto')
|
||||
axis_auto.set_label('自动', line_height=100)
|
||||
axis_auto.setCheckable(True)
|
||||
axis_auto.set_button_style('small')
|
||||
axis_auto.setChecked(True)
|
||||
axis_auto.clicked.connect(partial(undo(self.core.functions.apply_axis), 0))
|
||||
axis_x = Button(row.layout(), 'gsBindAxisX')
|
||||
axis_x.set_label('X', line_height=100)
|
||||
axis_x.setCheckable(True)
|
||||
axis_x.set_button_style('small')
|
||||
axis_x.clicked.connect(partial(undo(self.core.functions.apply_axis), 1))
|
||||
axis_y = Button(row.layout(), 'gsBindAxisY')
|
||||
axis_y.set_label('Y', line_height=100)
|
||||
axis_y.setCheckable(True)
|
||||
axis_y.set_button_style('small')
|
||||
axis_y.clicked.connect(partial(undo(self.core.functions.apply_axis), 2))
|
||||
axis_z = Button(row.layout(), 'gsBindAxisZ')
|
||||
axis_z.set_label('Z', line_height=100)
|
||||
axis_z.setCheckable(True)
|
||||
axis_z.set_button_style('small')
|
||||
axis_z.clicked.connect(partial(undo(self.core.functions.apply_axis), 3))
|
||||
axis_button_grp.addButton(axis_auto, 0)
|
||||
axis_button_grp.addButton(axis_x, 1)
|
||||
axis_button_grp.addButton(axis_y, 2)
|
||||
axis_button_grp.addButton(axis_z, 3)
|
||||
axis_flip = Button(row.layout(), 'AxisFlip')
|
||||
axis_flip.set_label('翻转', line_height=100)
|
||||
axis_flip.setCheckable(True)
|
||||
axis_flip.set_button_style('small')
|
||||
axis_flip.clicked.connect(partial(undo(self.core.functions.apply_axis), -1))
|
||||
column_layout.addWidget(separator())
|
||||
with Row(column.layout(), margins=style.scale([0, 0, 0, 0]), obj_name='originalCurvesRow') as original_curves_row:
|
||||
select_original_curves = Button(original_curves_row.layout(), 'selectOriginalCurves')
|
||||
select_original_curves.set_label('选择原始曲线', line_height=100)
|
||||
select_original_curves.set_button_style('small-filled')
|
||||
select_original_curves.clicked.connect(undo(self.core.curve_control.select_original_objects))
|
||||
edit_orig_obj = Button(original_curves_row.layout(), 'editOrigObj')
|
||||
edit_orig_obj.set_label('编辑原始对象', line_height=100)
|
||||
edit_orig_obj.setCheckable(True)
|
||||
edit_orig_obj.set_button_style('small')
|
||||
edit_orig_obj.clicked.connect(undo(self.core.curve_control.edit_original_objects))
|
||||
column_layout.addWidget(separator())
|
||||
with Row(layout, margins=style.scale([0, 0, 3, 0])) as length_divisions_row:
|
||||
length_division = ControlSlider(obj_name='lengthDivisions', typ='int')
|
||||
length_division.set_label('长度分段')
|
||||
length_division.set_min_max(2, 100)
|
||||
length_division.set_field_min_max(2, 10000)
|
||||
length_division.set_value(2)
|
||||
length_division.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, length_division))
|
||||
length_division.set_release_command(self.core.sliders.release)
|
||||
dynamic_divisions_toggle = Button(obj_name='dynamicDivisions')
|
||||
dynamic_divisions_toggle.set_label('自动')
|
||||
dynamic_divisions_toggle.setFixedWidth(style.scale(35))
|
||||
dynamic_divisions_toggle.set_button_style('small')
|
||||
dynamic_divisions_toggle.setCheckable(True)
|
||||
dynamic_divisions_toggle.clicked.connect(undo(self.core.functions.toggle_dynamic_divisions))
|
||||
length_division_layout = length_divisions_row.layout()
|
||||
if not isinstance(length_division_layout, QHBoxLayout):
|
||||
raise RuntimeError('未找到长度分段布局')
|
||||
length_division_layout.addWidget(length_division, 4)
|
||||
length_division_layout.addWidget(dynamic_divisions_toggle, 1)
|
||||
width_division = ControlSlider(layout, 'widthDivisions', 'int')
|
||||
width_division.set_label('宽度分段')
|
||||
width_division.set_min_max(2, 31)
|
||||
width_division.set_field_min_max(2, 10000)
|
||||
width_division.set_value(2)
|
||||
width_division.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, width_division))
|
||||
width_division.set_release_command(self.core.sliders.release)
|
||||
orientation = ControlSlider(layout, 'Orientation', 'float')
|
||||
orientation.set_label('方向')
|
||||
orientation.set_min_max(-180, 180)
|
||||
orientation.set_field_min_max(-36000, 36000)
|
||||
orientation.set_precision(1)
|
||||
orientation.set_step(0.5)
|
||||
orientation.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, orientation))
|
||||
orientation.set_release_command(self.core.sliders.release)
|
||||
twist = ControlSlider(layout, 'Twist', 'float')
|
||||
twist.set_label('扭曲')
|
||||
twist.set_min_max(-180, 180)
|
||||
twist.set_field_min_max(-36000, 36000)
|
||||
twist.set_precision(1)
|
||||
twist.set_step(0.5)
|
||||
twist.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, twist))
|
||||
twist.set_release_command(self.core.sliders.release)
|
||||
inv_twist = ControlSlider(layout, 'invTwist', 'float')
|
||||
inv_twist.set_label('反向扭曲')
|
||||
inv_twist.set_min_max(-180, 180)
|
||||
inv_twist.set_field_min_max(-36000, 36000)
|
||||
inv_twist.set_precision(1)
|
||||
inv_twist.set_step(0.5)
|
||||
inv_twist.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, inv_twist))
|
||||
inv_twist.set_release_command(self.core.sliders.release)
|
||||
with Frame(layout, 'twistCurveFrame', margins=[0, 1, 0, 2]) as twist_curve_frame:
|
||||
twist_curve_frame.get_frame_button().set_label('扭曲曲线图表')
|
||||
twist_graph = FallOffCurve(twist_curve_frame.get_frame_layout(), 'twistCurve')
|
||||
|
||||
def twist_graph_command(_):
|
||||
self.core.attributes.propagate_graphs(twist_graph)
|
||||
self.core.attributes.store_graphs(twist_graph)
|
||||
twist_graph.change_command(twist_graph_command)
|
||||
with Row(twist_curve_frame.get_frame_layout(), margins=style.scale([5, 0, 5, 2])) as row:
|
||||
row.setFixedHeight(int(style.BUTTON_HEIGHT))
|
||||
magnitude = FloatField('Magnitude', row.layout())
|
||||
magnitude.setFixedWidth(style.scale(45))
|
||||
magnitude.set_range(-99, 99)
|
||||
magnitude.set_step(0.01)
|
||||
magnitude.set_precision(2)
|
||||
magnitude.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, magnitude))
|
||||
magnitude.set_release_command(self.core.sliders.release)
|
||||
reset_button = Button(row.layout(), 'gsTwistGraphResetButton')
|
||||
reset_button.set_label('重置曲线')
|
||||
|
||||
def reset_twist_cmd():
|
||||
self.core.utils.reset_single_graph('twist')
|
||||
self.core.attributes.store_graphs(twist_graph)
|
||||
reset_button.clicked.connect(undo(reset_twist_cmd))
|
||||
pop_out_button = Button(row.layout(), 'gsTwistGraphPopOut')
|
||||
pop_out_button.setFixedWidth(style.scale(56))
|
||||
pop_out_button.set_label('^')
|
||||
pop_out_button.clicked.connect(self.twist_graph_pop_out)
|
||||
width = ControlSlider(layout, 'Width', 'float')
|
||||
width.set_label('宽度')
|
||||
width.set_min_max(0.001, 2)
|
||||
width.set_field_min_max(0.001, 1000)
|
||||
width.set_precision(3)
|
||||
width.set_step(0.001)
|
||||
width.set_value(1)
|
||||
width.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, width))
|
||||
width.set_release_command(self.core.sliders.release)
|
||||
taper = ControlSlider(layout, 'Taper', 'float')
|
||||
taper.set_label('锥度')
|
||||
taper.set_min_max(0.001, 3)
|
||||
taper.set_field_min_max(0.001, 1000)
|
||||
taper.set_precision(3)
|
||||
taper.set_step(0.001)
|
||||
taper.set_value(1)
|
||||
taper.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, taper))
|
||||
taper.set_release_command(self.core.sliders.release)
|
||||
with Row(layout, obj_name='widthComboSlider', spacing=0) as row:
|
||||
row_layout = row.layout()
|
||||
if not row_layout:
|
||||
raise RuntimeError('未找到行布局')
|
||||
with Column(row.layout()) as column:
|
||||
column_layout = column.layout()
|
||||
if not column_layout:
|
||||
raise RuntimeError('未找到列布局')
|
||||
width_x = ControlSlider(column_layout, 'WidthX', 'float')
|
||||
width_x.set_label('宽度 X')
|
||||
width_x.set_min_max(0.001, 2)
|
||||
width_x.set_field_min_max(0.001, 1000)
|
||||
width_x.set_precision(3)
|
||||
width_x.set_step(0.001)
|
||||
width_x.set_value(1)
|
||||
width_x.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, width_x))
|
||||
width_x.set_release_command(self.core.sliders.release)
|
||||
width_z = ControlSlider(column.layout(), 'WidthZ', 'float')
|
||||
width_z.set_label('宽度 Z')
|
||||
width_z.set_min_max(0.001, 2)
|
||||
width_z.set_field_min_max(0.001, 1000)
|
||||
width_z.set_precision(3)
|
||||
width_z.set_step(0.001)
|
||||
width_z.set_value(1)
|
||||
width_z.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, width_z))
|
||||
width_z.set_release_command(self.core.sliders.release)
|
||||
lock_button = IconCheckButton(row_layout, 'widthLockSwitch')
|
||||
lock_button.set_icons(GetFolder.icons() + 'sliderLock_en_reversed.png', GetFolder.icons() + 'sliderLock_reversed.png')
|
||||
lock_button.set_icon_width_height(10, 30)
|
||||
lock_button.setChecked(True)
|
||||
with Frame(layout, 'widthCurveFrame', margins=[0, 1, 0, 2]) as width_curve_frame:
|
||||
width_curve_frame.get_frame_button().set_label('宽度曲线图表')
|
||||
width_graph = FallOffCurve(width_curve_frame.get_frame_layout(), 'scaleCurve')
|
||||
|
||||
def width_graph_command(_):
|
||||
self.core.attributes.propagate_graphs(width_graph)
|
||||
self.core.attributes.store_graphs(width_graph)
|
||||
width_graph.change_command(width_graph_command)
|
||||
with Row(width_curve_frame.get_frame_layout(), margins=style.scale([5, 0, 5, 2])) as row:
|
||||
row.setFixedHeight(int(style.BUTTON_HEIGHT))
|
||||
reset_button = Button(row.layout(), 'gsWidthGraphResetButton')
|
||||
reset_button.set_label('重置曲线')
|
||||
|
||||
def reset_width_cmd():
|
||||
self.core.utils.reset_single_graph('scale')
|
||||
self.core.attributes.store_graphs(width_graph)
|
||||
reset_button.clicked.connect(undo(reset_width_cmd))
|
||||
pop_out_button = Button(row.layout(), 'gsWidthGraphPopOut')
|
||||
pop_out_button.setFixedWidth(style.scale(56))
|
||||
pop_out_button.set_label('^')
|
||||
pop_out_button.clicked.connect(self.width_graph_pop_out)
|
||||
length_unlock = Button(layout, 'LengthLock')
|
||||
length_unlock.set_label('长度解锁', line_height=100)
|
||||
length_unlock.setCheckable(True)
|
||||
length_unlock.set_button_style('small')
|
||||
length_unlock.setFixedWidth(style.scale(106))
|
||||
length_unlock.clicked.connect(partial(self.core.updates.curve_control_check_boxes, 2))
|
||||
length = ControlSlider(layout, 'Length', 'float')
|
||||
length.set_label('长度')
|
||||
length.set_min_max(0.001, 40)
|
||||
length.set_field_min_max(-1000, 1000)
|
||||
length.set_precision(3)
|
||||
length.set_step(0.001)
|
||||
length.set_value(20)
|
||||
length.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, length))
|
||||
length.set_release_command(self.core.sliders.release)
|
||||
offset = ControlSlider(layout, 'Offset', 'float')
|
||||
offset.set_label('偏移')
|
||||
offset.set_min_max(-1, 1)
|
||||
offset.set_field_min_max(-30, 30)
|
||||
offset.set_step(0.001)
|
||||
offset.set_value(1)
|
||||
offset.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, offset))
|
||||
offset.set_release_command(self.core.sliders.release)
|
||||
profile = ControlSlider(layout, 'Profile', 'float')
|
||||
profile.set_label('轮廓')
|
||||
profile.set_min_max(-2, 2)
|
||||
profile.set_field_min_max(-1000, 1000)
|
||||
profile.set_step(0.001)
|
||||
profile.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, profile))
|
||||
profile.set_release_command(self.core.sliders.release)
|
||||
with Frame(layout, 'profileCurveGraph', margins=[0, 1, 0, 2]) as profile_curve_frame:
|
||||
profile_curve_frame.get_frame_button().set_label('轮廓曲线图表')
|
||||
profile_graph = FallOffCurve(profile_curve_frame.get_frame_layout(), 'profileCurve', attr=False)
|
||||
|
||||
def change_command(value):
|
||||
self.core.attributes.update_lattice(value)
|
||||
self.core.attributes.equalize_profile_curve()
|
||||
self.core.attributes.store_graphs(profile_graph)
|
||||
profile_graph.change_command(change_command)
|
||||
profile_smoothing_slider = ControlSlider(profile_curve_frame.get_frame_layout(), 'profileSmoothing', 'int')
|
||||
profile_smoothing_slider.set_label('平滑度')
|
||||
profile_smoothing_slider.set_min_max(2, 30)
|
||||
profile_smoothing_slider.set_value(2)
|
||||
profile_smoothing_slider.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, profile_smoothing_slider))
|
||||
profile_smoothing_slider.set_release_command(self.core.sliders.release)
|
||||
with Row(profile_curve_frame.get_frame_layout(), margins=style.scale([5, 0, 5, 2])) as row:
|
||||
row.setFixedHeight(int(style.BUTTON_HEIGHT))
|
||||
with Column(row.layout(), spacing=0) as eq_column:
|
||||
eq_column.setFixedHeight(style.scale(24))
|
||||
with Row(eq_column.layout(), spacing=0) as eq_row:
|
||||
|
||||
def toggle_eq():
|
||||
self.widget_manager.get('equalizeCurveButton').setDisabled(self.widget_manager.get('autoEqualizeSwitchOn').isChecked())
|
||||
auto_equalize_group = QtWidgets.QButtonGroup(eq_row)
|
||||
auto_equalize_group.buttonClicked.connect(toggle_eq)
|
||||
auto_equalize_group.buttonClicked.connect(undo(self.core.attributes.equalize_profile_curve))
|
||||
auto_equalize = Button(eq_row.layout(), 'autoEqualizeSwitchOn')
|
||||
auto_equalize.set_label('自动', line_height=100)
|
||||
auto_equalize.set_label_style('small')
|
||||
auto_equalize.set_button_style('small-compound-top-left')
|
||||
auto_equalize.setCheckable(True)
|
||||
auto_equalize.setChecked(True)
|
||||
auto_equalize_off = Button(eq_row.layout(), 'autoEqualizeSwitchOff')
|
||||
auto_equalize_off.set_label('手动', line_height=100)
|
||||
auto_equalize_off.set_label_style('small')
|
||||
auto_equalize_off.set_button_style('small-compound-top-right')
|
||||
auto_equalize_off.setCheckable(True)
|
||||
auto_equalize_group.addButton(auto_equalize)
|
||||
auto_equalize_group.addButton(auto_equalize_off)
|
||||
equalize_profile_curve = Button(eq_column.layout(), 'equalizeCurveButton')
|
||||
equalize_profile_curve.set_label('均衡曲线', line_height=100)
|
||||
equalize_profile_curve.set_label_style('small')
|
||||
equalize_profile_curve.set_button_style('small-filled-compound-bottom')
|
||||
equalize_profile_curve.setDisabled(True)
|
||||
equalize_profile_curve.clicked.connect(partial(undo(self.core.attributes.equalize_profile_curve), True))
|
||||
reset_button = Button(obj_name='gsResetProfileGraphButton')
|
||||
reset_button.set_label('重置曲线')
|
||||
|
||||
def reset_button_clicked(*_):
|
||||
self.core.attributes.reset_profile_curve()
|
||||
self.core.attributes.store_graphs(profile_graph)
|
||||
reset_button.clicked.connect(undo(reset_button_clicked))
|
||||
pop_out_button = Button(obj_name='gsProfileGraphPopOut')
|
||||
pop_out_button.set_label('^')
|
||||
pop_out_button.clicked.connect(self.profile_graph_pop_out)
|
||||
row_layout = row.layout()
|
||||
if not isinstance(row_layout, QHBoxLayout):
|
||||
raise RuntimeError('未找到行布局')
|
||||
row_layout.addWidget(eq_column, 2)
|
||||
row_layout.addWidget(reset_button, 3)
|
||||
row_layout.addWidget(pop_out_button, 1)
|
||||
normals = ControlSlider(layout, 'surfaceNormals', 'float')
|
||||
normals.set_label('法线')
|
||||
normals.set_min_max(0, 180)
|
||||
normals.set_precision(1)
|
||||
normals.set_value(180)
|
||||
normals.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, normals))
|
||||
normals.set_release_command(self.core.sliders.release)
|
||||
reverse_normals = Button(layout, 'reverseNormals')
|
||||
reverse_normals.set_label('反转法线', line_height=100)
|
||||
reverse_normals.setCheckable(True)
|
||||
reverse_normals.set_button_style('small')
|
||||
reverse_normals.setFixedWidth(style.scale(106))
|
||||
reverse_normals.clicked.connect(partial(self.core.updates.curve_control_check_boxes, 0))
|
||||
with Frame(layout, 'otherFrame', label='其他', margins=[2, 2, 2, 2]) as refine_frame, Row(refine_frame.get_frame_layout(), margins=style.scale([0, 0, 3, 0])) as curve_sampling_row:
|
||||
sampling_accuracy = ControlSlider(layout, 'samplingAccuracy', 'float')
|
||||
sampling_accuracy.set_label('采样精度')
|
||||
sampling_accuracy.set_min_max(0.001, 2)
|
||||
sampling_accuracy.set_step(0.01)
|
||||
sampling_accuracy.set_value(0.33)
|
||||
sampling_accuracy.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, sampling_accuracy))
|
||||
sampling_accuracy.set_release_command(self.core.sliders.release)
|
||||
auto_sampling = Button(obj_name='autoSampling')
|
||||
auto_sampling.set_label('自动')
|
||||
auto_sampling.setFixedWidth(style.scale(35))
|
||||
auto_sampling.set_button_style('small')
|
||||
auto_sampling.setCheckable(True)
|
||||
auto_sampling.clicked.connect(undo(self.core.functions.toggle_auto_sampling))
|
||||
curve_sampling_row_layout = curve_sampling_row.layout()
|
||||
if not isinstance(curve_sampling_row_layout, QHBoxLayout):
|
||||
raise RuntimeError('未找到曲线采样行布局')
|
||||
curve_sampling_row_layout.addWidget(sampling_accuracy, 4)
|
||||
curve_sampling_row_layout.addWidget(auto_sampling, 1)
|
||||
with Row(refine_frame.get_frame_layout(), margins=style.scale([0, 0, 3, 0])) as curve_refine_row:
|
||||
refine = ControlSlider(layout, 'curveRefine', 'int')
|
||||
refine.set_label('细化')
|
||||
refine.set_min_max(0, 100)
|
||||
refine.set_field_min_max(0, 10000)
|
||||
refine.set_value(20)
|
||||
refine.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, refine))
|
||||
refine.set_release_command(self.core.sliders.release)
|
||||
auto_refine_toggle = Button(obj_name='autoRefine')
|
||||
auto_refine_toggle.set_label('自动')
|
||||
auto_refine_toggle.setFixedWidth(style.scale(35))
|
||||
auto_refine_toggle.set_button_style('small')
|
||||
auto_refine_toggle.setCheckable(True)
|
||||
auto_refine_toggle.clicked.connect(undo(self.core.functions.toggle_auto_refine))
|
||||
curve_refine_row_layout = curve_refine_row.layout()
|
||||
if not isinstance(curve_refine_row_layout, QHBoxLayout):
|
||||
raise RuntimeError('未找到曲线细化行布局')
|
||||
curve_refine_row_layout.addWidget(refine, 4)
|
||||
curve_refine_row_layout.addWidget(auto_refine_toggle, 1)
|
||||
smooth = ControlSlider(refine_frame.get_frame_layout(), 'curveSmooth', 'float')
|
||||
smooth.setVisible(False)
|
||||
smooth.set_label('平滑')
|
||||
smooth.set_min_max(0, 10)
|
||||
smooth.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, smooth))
|
||||
smooth.set_release_command(self.core.sliders.release)
|
||||
with Frame(layout, 'orientToNormalsFrame', label='朝向法线', margins=[2, 2, 2, 2]) as orient_frame:
|
||||
orient_frame.setVisible(False)
|
||||
|
||||
def select_mesh():
|
||||
sel = mc.filterExpand(mc.ls(sl=1, l=1), sm=12)
|
||||
if not sel:
|
||||
sel = mc.ls(hl=1, o=1, l=1)
|
||||
if not sel:
|
||||
logger.warning('选择兼容的网格。', in_view=True)
|
||||
else:
|
||||
self.mesh_name.setText(sel[0])
|
||||
with Row(orient_frame.get_frame_layout()) as row:
|
||||
select_mesh_btn = Button(row.layout(), 'gsOrientToNormalsSelectTarget')
|
||||
select_mesh_btn.setFixedWidth(style.scale(100))
|
||||
select_mesh_btn.set_label('选择目标')
|
||||
select_mesh_btn.clicked.connect(select_mesh)
|
||||
self.mesh_name = LineEdit('gsOrientMeshName', row.layout())
|
||||
self.mesh_name.setPlaceholderText('选择或输入目标网格')
|
||||
self.mesh_name.setClearButtonEnabled(True)
|
||||
orient_frame.get_frame_layout().addWidget(separator())
|
||||
with Row(orient_frame.get_frame_layout()) as row:
|
||||
iterations_slider = ControlSlider(row.layout(), 'gsIterationsSlider', 'int')
|
||||
iterations_slider.set_min_max(1, 100)
|
||||
iterations_slider.set_value(10)
|
||||
iterations_slider.set_label('迭代次数')
|
||||
auto_refresh_button = Button(row.layout(), 'orientRefreshViewport')
|
||||
auto_refresh_button.set_button_style('small')
|
||||
auto_refresh_button.setCheckable(True)
|
||||
auto_refresh_button.setChecked(True)
|
||||
auto_refresh_button.set_width_height(width=style.scale(60))
|
||||
auto_refresh_button.set_label('刷新视图', line_height=100)
|
||||
with Row(orient_frame.get_frame_layout()) as row:
|
||||
min_angle_slider = ControlSlider(row.layout(), 'gsMinimumAngle', 'float')
|
||||
min_angle_slider.set_min_max(0.1, 90)
|
||||
min_angle_slider.set_value(1)
|
||||
min_angle_slider.set_label('最小角度')
|
||||
orient_frame.get_frame_layout().addWidget(separator())
|
||||
with Row(orient_frame.get_frame_layout()) as row:
|
||||
orient = Button(row.layout(), 'gsOrientToNormals')
|
||||
orient.set_label('朝向')
|
||||
orient.clicked.connect(undo(self.core.functions.orient_to_face_normals))
|
||||
with Frame(layout, 'solidifyFrame', label='实体化控制', margins=[2, 2, 2, 2]) as solidify_frame:
|
||||
solidify = Button(solidify_frame.get_frame_layout(), 'solidify')
|
||||
solidify.set_label('实体化', line_height=100)
|
||||
solidify.setCheckable(True)
|
||||
solidify.set_button_style('small')
|
||||
solidify.setFixedWidth(style.scale(106))
|
||||
solidify.clicked.connect(partial(self.core.updates.curve_control_check_boxes, 1))
|
||||
thickness = ControlSlider(solidify_frame.get_frame_layout(), 'solidifyThickness', 'float')
|
||||
thickness.set_label('厚度')
|
||||
thickness.set_min_max(0.001, 5)
|
||||
thickness.set_field_min_max(-100, 100)
|
||||
thickness.set_value(0.25)
|
||||
thickness.set_step(0.001)
|
||||
thickness.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, thickness))
|
||||
thickness.set_release_command(self.core.sliders.release)
|
||||
divisions = ControlSlider(solidify_frame.get_frame_layout(), 'solidifyDivisions', 'int')
|
||||
divisions.set_label('分段数')
|
||||
divisions.set_min_max(0, 10)
|
||||
divisions.set_field_min_max(0, 100)
|
||||
divisions.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, divisions))
|
||||
divisions.set_release_command(self.core.sliders.release)
|
||||
solidify_scale_x = ControlSlider(solidify_frame.get_frame_layout(), 'solidifyScaleX', 'float')
|
||||
solidify_scale_x.set_label('缩放 X')
|
||||
solidify_scale_x.set_min_max(-10, 10)
|
||||
solidify_scale_x.set_field_min_max(-100, 100)
|
||||
solidify_scale_x.set_step(0.001)
|
||||
solidify_scale_x.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, solidify_scale_x))
|
||||
solidify_scale_x.set_release_command(self.core.sliders.release)
|
||||
solidify_scale_y = ControlSlider(solidify_frame.get_frame_layout(), 'solidifyScaleY', 'float')
|
||||
solidify_scale_y.set_label('缩放 Y')
|
||||
solidify_scale_y.set_min_max(-10, 10)
|
||||
solidify_scale_y.set_field_min_max(-100, 100)
|
||||
solidify_scale_y.set_step(0.001)
|
||||
solidify_scale_y.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, solidify_scale_y))
|
||||
solidify_scale_y.set_release_command(self.core.sliders.release)
|
||||
offset = ControlSlider(solidify_frame.get_frame_layout(), 'solidifyOffset', 'float')
|
||||
offset.set_label('偏移')
|
||||
offset.set_min_max(-1, 1)
|
||||
offset.set_field_min_max(-100, 100)
|
||||
offset.set_step(0.001)
|
||||
offset.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, offset))
|
||||
offset.set_release_command(self.core.sliders.release)
|
||||
solidify_normals = ControlSlider(solidify_frame.get_frame_layout(), 'solidifyNormals', 'float')
|
||||
solidify_normals.set_label('实体化法线')
|
||||
solidify_normals.set_min_max(0, 180)
|
||||
solidify_normals.set_precision(1)
|
||||
solidify_normals.set_value(180)
|
||||
solidify_normals.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, solidify_normals))
|
||||
solidify_normals.set_release_command(self.core.sliders.release)
|
||||
with Frame(layout, 'UVFrame', label='UV 控制', margins=[2, 2, 2, 2]) as uv_frame:
|
||||
flip_uv = Button(uv_frame.get_frame_layout(), 'flipUV')
|
||||
flip_uv.set_label('水平翻转 UV', line_height=100)
|
||||
flip_uv.setCheckable(True)
|
||||
flip_uv.set_button_style('small')
|
||||
flip_uv.setFixedWidth(style.scale(106))
|
||||
flip_uv.clicked.connect(partial(undo(self.core.updates.curve_control_check_boxes), 3))
|
||||
move_u = ControlSlider(uv_frame.get_frame_layout(), 'moveU', 'float')
|
||||
move_u.set_label('移动 U')
|
||||
move_u.set_min_max(-0.5, 0.5)
|
||||
move_u.set_step(0.001)
|
||||
move_u.set_field_min_max(-100, 100)
|
||||
move_u.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, move_u))
|
||||
move_u.set_release_command(self.core.sliders.release)
|
||||
move_v = ControlSlider(uv_frame.get_frame_layout(), 'moveV', 'float')
|
||||
move_v.set_label('移动 V')
|
||||
move_v.set_min_max(-0.5, 0.5)
|
||||
move_v.set_step(0.001)
|
||||
move_v.set_field_min_max(-100, 100)
|
||||
move_v.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, move_v))
|
||||
move_v.set_release_command(self.core.sliders.release)
|
||||
scale_u = ControlSlider(uv_frame.get_frame_layout(), 'scaleU', 'float')
|
||||
scale_u.set_label('缩放 U')
|
||||
scale_u.set_min_max(0.001, 1.999)
|
||||
scale_u.set_field_min_max(0, 100)
|
||||
scale_u.set_step(0.001)
|
||||
scale_u.set_value(1)
|
||||
scale_u.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, scale_u))
|
||||
scale_u.set_release_command(self.core.sliders.release)
|
||||
scale_v = ControlSlider(uv_frame.get_frame_layout(), 'scaleV', 'float')
|
||||
scale_v.set_label('缩放 V')
|
||||
scale_v.set_min_max(0.001, 1.999)
|
||||
scale_v.set_field_min_max(0, 100)
|
||||
scale_v.set_step(0.001)
|
||||
scale_v.set_value(1)
|
||||
scale_v.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, scale_v))
|
||||
scale_v.set_release_command(self.core.sliders.release)
|
||||
rotate_uv = ControlSlider(uv_frame.get_frame_layout(), 'rotateUV', 'float')
|
||||
rotate_uv.set_label('旋转 UV')
|
||||
rotate_uv.set_min_max(-180, 180)
|
||||
rotate_uv.set_field_min_max(-3600, 3600)
|
||||
rotate_uv.set_precision(2)
|
||||
rotate_uv.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, rotate_uv))
|
||||
rotate_uv.set_release_command(self.core.sliders.release)
|
||||
rotate_uv_root = ControlSlider(uv_frame.get_frame_layout(), 'rotateRootUV', 'float')
|
||||
rotate_uv_root.set_label('旋转根部 UV')
|
||||
rotate_uv_root.set_min_max(-180, 180)
|
||||
rotate_uv_root.set_field_min_max(-3600, 3600)
|
||||
rotate_uv_root.set_precision(2)
|
||||
rotate_uv_root.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, rotate_uv_root))
|
||||
rotate_uv_root.set_release_command(self.core.sliders.release)
|
||||
rotate_uv_root.setVisible(False)
|
||||
rotate_uv_tip = ControlSlider(uv_frame.get_frame_layout(), 'rotateTipUV', 'float')
|
||||
rotate_uv_tip.set_label('旋转尖端 UV')
|
||||
rotate_uv_tip.set_min_max(-180, 180)
|
||||
rotate_uv_tip.set_field_min_max(-3600, 3600)
|
||||
rotate_uv_tip.set_precision(2)
|
||||
rotate_uv_tip.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, rotate_uv_tip))
|
||||
rotate_uv_tip.set_release_command(self.core.sliders.release)
|
||||
rotate_uv_tip.setVisible(False)
|
||||
if MAYA_VER in (2020, 2022) and (not self.options_manager.get('UVBugMessageDismissed')):
|
||||
with Row(uv_frame.get_frame_layout()) as row:
|
||||
label = Label(row.layout())
|
||||
label.set_label('<p style="color:#FF542F">如何修复 Maya 2020 - 2022 的 UV 问题:</p>')
|
||||
link_button = Button(row.layout())
|
||||
link_button.set_label('打开修复方法')
|
||||
link_button.set_button_style('small-filled')
|
||||
link_button.clicked.connect(lambda: open_link('https://gs-curvetools.readthedocs.io/en/latest/faq.html#maya-2020-2022-and-broken-uvs'))
|
||||
dismiss_message = Button(row.layout())
|
||||
dismiss_message.set_label('忽略提示')
|
||||
dismiss_message.set_button_style('small-filled')
|
||||
dismiss_message.clicked.connect(lambda: row.setHidden(True))
|
||||
dismiss_message.clicked.connect(lambda: self.options_manager.set('UVBugMessageDismissed', 1))
|
||||
with Frame(layout, 'advancedVisibilityFrame', label='高级可见性设置', margins=style.scale([3, 2, 3, 2]), spacing=style.scale(3)) as visibility_frame:
|
||||
with Row(visibility_frame.get_frame_layout()) as row:
|
||||
geometry_highlight = Button(row.layout(), 'geometryHighlight')
|
||||
geometry_highlight.set_label('几何体高亮显示', line_height=100)
|
||||
geometry_highlight.setCheckable(True)
|
||||
geometry_highlight.setChecked(bool(self.options_manager.get('GeometryHighlightEnabled')))
|
||||
geometry_highlight.set_button_style('small')
|
||||
geometry_highlight.clicked.connect(no_undo(self.core.advanced_visibility.geometry_highlight_command))
|
||||
curve_highlight = Button(row.layout(), 'curveHighlight')
|
||||
curve_highlight.set_label('曲线高亮显示', line_height=100)
|
||||
curve_highlight.setCheckable(True)
|
||||
curve_highlight.set_button_style('small')
|
||||
curve_highlight.setChecked(False)
|
||||
curve_highlight.clicked.connect(undo(self.core.advanced_visibility.toggle_curve_highlight_from_ui))
|
||||
visibility_frame.get_frame_layout().addWidget(separator())
|
||||
|
||||
def slider_change_command(*_):
|
||||
self.core.advanced_visibility.apply_settings_to_node()
|
||||
self.core.advanced_visibility.save_settings_from_ui()
|
||||
with Column(visibility_frame.get_frame_layout(), obj_name='gsCurveHighlightFrame', spacing=style.scale(4)) as column:
|
||||
column.setEnabled(False)
|
||||
column_layout = column.layout()
|
||||
if not isinstance(column_layout, QVBoxLayout):
|
||||
raise RuntimeError('未找到列布局')
|
||||
with Row(column.layout(), spacing=4) as row:
|
||||
cv_size_label = Label()
|
||||
cv_size_label.set_label('控制点大小:')
|
||||
cv_size_label.setFixedWidth(style.scale(100))
|
||||
cv_size_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
point_size_slider = maya_slider(mc.floatSliderGrp('gsPointSizeSlider', f=1, adj=2, w=1, cw=(1, 45), min=1, max=100, v=10, dc=lambda _: no_undo(self.core.advanced_visibility.apply_settings_to_node)(), cc=no_undo(slider_change_command)))
|
||||
self.widget_manager.add('gsPointSizeSlider', point_size_slider)
|
||||
row_layout = row.layout()
|
||||
if not isinstance(row_layout, QHBoxLayout):
|
||||
raise RuntimeError('未找到行布局')
|
||||
row_layout.addWidget(cv_size_label, 0)
|
||||
row_layout.addWidget(point_size_slider, 1)
|
||||
with Row(column.layout(), spacing=style.scale(4)) as row:
|
||||
selected_color_label = Label()
|
||||
selected_color_label.set_label('选中颜色:')
|
||||
selected_color_label.setFixedWidth(style.scale(100))
|
||||
selected_color_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
selected_color_picker = ColorPicker('gsSelectedCVColor')
|
||||
selected_color_picker.connect_drag_command(lambda *_: no_undo(self.core.advanced_visibility.apply_settings_to_node)())
|
||||
selected_color_picker.connect_command(no_undo(slider_change_command))
|
||||
selected_alpha_value = FloatField('gsSelectedCVAlpha')
|
||||
selected_alpha_value.setFixedWidth(style.scale(45))
|
||||
selected_alpha_value.set_value(1.0)
|
||||
selected_alpha_value.set_range(0, 1)
|
||||
selected_alpha_value.set_step(0.01)
|
||||
selected_alpha_value.set_precision(2)
|
||||
selected_alpha_value.set_drag_command(lambda *_: no_undo(self.core.advanced_visibility.apply_settings_to_node)())
|
||||
selected_alpha_value.set_release_command(no_undo(slider_change_command))
|
||||
row_layout = row.layout()
|
||||
if not isinstance(row_layout, QHBoxLayout):
|
||||
raise RuntimeError('未找到行布局')
|
||||
row_layout.addWidget(selected_color_label, 0)
|
||||
row_layout.addWidget(selected_color_picker, 1)
|
||||
row_layout.addWidget(selected_alpha_value, 0)
|
||||
with Row(column.layout(), spacing=style.scale(4)) as row:
|
||||
deselected_color_label = Label()
|
||||
deselected_color_label.set_label('未选中颜色:')
|
||||
deselected_color_label.setFixedWidth(style.scale(100))
|
||||
deselected_color_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
deselected_color_picker = ColorPicker('gsDeselectedCVColor')
|
||||
deselected_color_picker.connect_drag_command(lambda *_: no_undo(self.core.advanced_visibility.apply_settings_to_node)())
|
||||
deselected_color_picker.connect_command(no_undo(slider_change_command))
|
||||
deselected_alpha_value = FloatField('gsDeselectedCVAlpha')
|
||||
deselected_alpha_value.setFixedWidth(style.scale(45))
|
||||
deselected_alpha_value.set_value(1)
|
||||
deselected_alpha_value.set_range(0, 1)
|
||||
deselected_alpha_value.set_step(0.01)
|
||||
deselected_alpha_value.set_precision(2)
|
||||
deselected_alpha_value.set_drag_command(lambda *_: no_undo(self.core.advanced_visibility.apply_settings_to_node)())
|
||||
deselected_alpha_value.set_release_command(no_undo(slider_change_command))
|
||||
row_layout = row.layout()
|
||||
if not isinstance(row_layout, QHBoxLayout):
|
||||
raise RuntimeError('未找到行布局')
|
||||
row_layout.addWidget(deselected_color_label, 0)
|
||||
row_layout.addWidget(deselected_color_picker, 1)
|
||||
row_layout.addWidget(deselected_alpha_value, 1)
|
||||
column_layout.addWidget(separator())
|
||||
curve_visibility = Button(column.layout(), 'curveVisibility')
|
||||
curve_visibility.set_label('曲线可见性', line_height=100)
|
||||
curve_visibility.setCheckable(True)
|
||||
curve_visibility.set_button_style('small')
|
||||
curve_visibility.setChecked(True)
|
||||
curve_visibility.clicked.connect(no_undo(slider_change_command))
|
||||
with Row(column.layout(), spacing=4) as row:
|
||||
curve_width_label = Label()
|
||||
curve_width_label.set_label('曲线宽度:')
|
||||
curve_width_label.setFixedWidth(style.scale(100))
|
||||
curve_width_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
curve_width_slider = maya_slider(mc.floatSliderGrp('gsCurveWidthSlider', f=1, adj=2, w=1, cw=(1, 45), min=1, max=100, v=4, dc=lambda _: no_undo(self.core.advanced_visibility.apply_settings_to_node)(), cc=no_undo(slider_change_command)))
|
||||
self.widget_manager.add('gsCurveWidthSlider', curve_width_slider)
|
||||
row_layout = row.layout()
|
||||
if not isinstance(row_layout, QHBoxLayout):
|
||||
raise RuntimeError('未找到行布局')
|
||||
row_layout.addWidget(curve_width_label, 0)
|
||||
row_layout.addWidget(curve_width_slider, 1)
|
||||
with Row(column.layout(), spacing=style.scale(4)) as row:
|
||||
curve_highlight_color_label = Label()
|
||||
curve_highlight_color_label.set_label('曲线颜色:')
|
||||
curve_highlight_color_label.setFixedWidth(style.scale(100))
|
||||
curve_highlight_color_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
curve_highlight_color = ColorPicker('gsCurveHighlightColor')
|
||||
curve_highlight_color.connect_drag_command(lambda *_: no_undo(self.core.advanced_visibility.apply_settings_to_node)())
|
||||
curve_highlight_color.connect_command(no_undo(slider_change_command))
|
||||
curve_highlight_alpha = FloatField('gsCurveHighlightAlpha')
|
||||
curve_highlight_alpha.setFixedWidth(style.scale(45))
|
||||
curve_highlight_alpha.set_value(1.0)
|
||||
curve_highlight_alpha.set_range(0, 1)
|
||||
curve_highlight_alpha.set_step(0.01)
|
||||
curve_highlight_alpha.set_precision(2)
|
||||
curve_highlight_alpha.set_drag_command(lambda *_: no_undo(self.core.advanced_visibility.apply_settings_to_node)())
|
||||
curve_highlight_alpha.set_release_command(no_undo(slider_change_command))
|
||||
row_layout = row.layout()
|
||||
if not isinstance(row_layout, QHBoxLayout):
|
||||
raise RuntimeError('未找到行布局')
|
||||
row_layout.addWidget(curve_highlight_color_label, 0)
|
||||
row_layout.addWidget(curve_highlight_color, 1)
|
||||
row_layout.addWidget(curve_highlight_alpha, 1)
|
||||
column_layout.addWidget(separator())
|
||||
hull_visibility = Button(column.layout(), 'hullVisibility')
|
||||
hull_visibility.set_label('壳线可见性', line_height=100)
|
||||
hull_visibility.setCheckable(True)
|
||||
hull_visibility.set_button_style('small')
|
||||
hull_visibility.clicked.connect(no_undo(slider_change_command))
|
||||
with Row(column.layout(), spacing=4) as row:
|
||||
hull_width_label = Label()
|
||||
hull_width_label.set_label('壳线宽度:')
|
||||
hull_width_label.setFixedWidth(style.scale(100))
|
||||
hull_width_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
hull_width_slider = maya_slider(mc.floatSliderGrp('gsHullWidthSlider', f=1, adj=2, w=1, cw=(1, 45), min=1, max=100, v=3, dc=lambda _: no_undo(self.core.advanced_visibility.apply_settings_to_node)(), cc=no_undo(slider_change_command)))
|
||||
self.widget_manager.add('gsHullWidthSlider', hull_width_slider)
|
||||
row_layout = row.layout()
|
||||
if not isinstance(row_layout, QHBoxLayout):
|
||||
raise RuntimeError('未找到行布局')
|
||||
row_layout.addWidget(hull_width_label, 0)
|
||||
row_layout.addWidget(hull_width_slider, 1)
|
||||
with Row(column.layout(), spacing=style.scale(4)) as row:
|
||||
hull_color_label = Label()
|
||||
hull_color_label.set_label('壳线颜色:')
|
||||
hull_color_label.setFixedWidth(style.scale(100))
|
||||
hull_color_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
hull_color = ColorPicker('gsHullHighlightColor')
|
||||
hull_color.connect_drag_command(lambda *_: no_undo(self.core.advanced_visibility.apply_settings_to_node)())
|
||||
hull_color.connect_command(no_undo(slider_change_command))
|
||||
hull_alpha = FloatField('gsHullHighlightAlpha')
|
||||
hull_alpha.setFixedWidth(style.scale(45))
|
||||
hull_alpha.set_value(1.0)
|
||||
hull_alpha.set_range(0, 1)
|
||||
hull_alpha.set_step(0.01)
|
||||
hull_alpha.set_precision(2)
|
||||
hull_alpha.set_drag_command(lambda *_: no_undo(self.core.advanced_visibility.apply_settings_to_node)())
|
||||
hull_alpha.set_release_command(no_undo(slider_change_command))
|
||||
row_layout = row.layout()
|
||||
if not isinstance(row_layout, QHBoxLayout):
|
||||
raise RuntimeError('未找到行布局')
|
||||
row_layout.addWidget(hull_color_label, 0)
|
||||
row_layout.addWidget(hull_color, 1)
|
||||
row_layout.addWidget(hull_alpha, 1)
|
||||
column_layout.addWidget(separator())
|
||||
with Frame(column.layout(), 'advancedVisibilityFrame', label='其他选项:', margins=style.scale([3, 2, 3, 2]), spacing=style.scale(3)) as options_frame:
|
||||
with Row(options_frame.get_frame_layout()) as row:
|
||||
lazy_update = Button(row.layout(), 'lazyUpdate')
|
||||
lazy_update.set_label('延迟更新', line_height=100)
|
||||
lazy_update.setCheckable(True)
|
||||
lazy_update.set_button_style('small')
|
||||
lazy_update.clicked.connect(no_undo(slider_change_command))
|
||||
always_on_top = Button(row.layout(), 'alwaysOnTop')
|
||||
always_on_top.set_label('始终置顶', line_height=100)
|
||||
always_on_top.setCheckable(True)
|
||||
always_on_top.set_button_style('small')
|
||||
always_on_top.setChecked(True)
|
||||
always_on_top.clicked.connect(no_undo(slider_change_command))
|
||||
options_frame.get_frame_layout().addWidget(separator())
|
||||
experimental_label = Label(options_frame.get_frame_layout())
|
||||
experimental_label.set_label('距离颜色设置:')
|
||||
with Row(options_frame.get_frame_layout()) as row:
|
||||
curve_distance_color = Button(row.layout(), 'curveDistanceColor')
|
||||
curve_distance_color.set_label('曲线', line_height=100)
|
||||
curve_distance_color.setCheckable(True)
|
||||
curve_distance_color.set_button_style('small')
|
||||
curve_distance_color.setChecked(True)
|
||||
curve_distance_color.clicked.connect(no_undo(slider_change_command))
|
||||
cv_distance_color = Button(row.layout(), 'cvDistanceColor')
|
||||
cv_distance_color.set_label('控制点', line_height=100)
|
||||
cv_distance_color.setCheckable(True)
|
||||
cv_distance_color.set_button_style('small')
|
||||
cv_distance_color.setChecked(True)
|
||||
cv_distance_color.clicked.connect(no_undo(slider_change_command))
|
||||
hull_distance_color = Button(row.layout(), 'hullDistanceColor')
|
||||
hull_distance_color.set_label('壳线', line_height=100)
|
||||
hull_distance_color.setCheckable(True)
|
||||
hull_distance_color.set_button_style('small')
|
||||
hull_distance_color.setChecked(True)
|
||||
hull_distance_color.clicked.connect(no_undo(slider_change_command))
|
||||
with Row(options_frame.get_frame_layout()) as row:
|
||||
distance_color_min_label = Label()
|
||||
distance_color_min_label.set_label('颜色最小值:')
|
||||
distance_color_min_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
distance_color_min_input = FloatField('gsDistanceColorMinValue')
|
||||
distance_color_min_input.set_value(0.25)
|
||||
distance_color_min_input.set_range(0.01, 1)
|
||||
distance_color_min_input.set_step(0.01)
|
||||
distance_color_min_input.set_precision(2)
|
||||
distance_color_min_input.set_drag_command(lambda *_: no_undo(self.core.advanced_visibility.apply_settings_to_node)())
|
||||
distance_color_min_input.set_release_command(no_undo(slider_change_command))
|
||||
distance_color_max_label = Label()
|
||||
distance_color_max_label.set_label('颜色最大值:')
|
||||
distance_color_max_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
distance_color_max_input = FloatField('gsDistanceColorMaxValue')
|
||||
distance_color_max_input.set_value(1.0)
|
||||
distance_color_max_input.set_range(0.01, 1)
|
||||
distance_color_max_input.set_step(0.01)
|
||||
distance_color_max_input.set_precision(2)
|
||||
distance_color_max_input.set_drag_command(lambda *_: no_undo(self.core.advanced_visibility.apply_settings_to_node)())
|
||||
distance_color_max_input.set_release_command(no_undo(slider_change_command))
|
||||
row_layout = row.layout()
|
||||
if not isinstance(row_layout, QHBoxLayout):
|
||||
raise RuntimeError('未找到行布局')
|
||||
row_layout.addWidget(distance_color_min_label, 1)
|
||||
row_layout.addWidget(distance_color_min_input, 1)
|
||||
row_layout.addWidget(distance_color_max_label, 1)
|
||||
row_layout.addWidget(distance_color_max_input, 1)
|
||||
options_frame.get_frame_layout().addWidget(separator())
|
||||
experimental_label = Label(options_frame.get_frame_layout())
|
||||
experimental_label.set_label('实验性功能(可能较慢):')
|
||||
cv_occlusion = Button(options_frame.get_frame_layout(), 'CVocclusion')
|
||||
cv_occlusion.set_label('启用控制点遮挡效果', line_height=100)
|
||||
cv_occlusion.setCheckable(True)
|
||||
cv_occlusion.set_button_style('small')
|
||||
cv_occlusion.clicked.connect(no_undo(slider_change_command))
|
||||
with Row(options_frame.get_frame_layout(), spacing=style.scale(4)) as row:
|
||||
select_occluder_button = Button(row.layout(), 'gsSelectOccluderButton')
|
||||
select_occluder_button.setFixedWidth(style.scale(100))
|
||||
select_occluder_button.set_label('选择遮挡物')
|
||||
select_occluder_button.clicked.connect(no_undo(self.core.advanced_visibility.select_occluder_from_scene))
|
||||
occluder_mesh_name = LineEdit('gsOccluderMeshName', row.layout())
|
||||
occluder_mesh_name.setPlaceholderText('选择或输入遮挡物网格名称')
|
||||
occluder_mesh_name.setClearButtonEnabled(True)
|
||||
occluder_mesh_name.editingFinished.connect(no_undo(slider_change_command))
|
||||
self.core.advanced_visibility.load_settings_from_option_var()
|
||||
self.tooltips.toggle_custom_tooltips_curve_control(self.options_manager.get('enableTooltips'))
|
||||
layout.addWidget(separator())
|
||||
reset_button = Button(layout, 'resetControlSliders')
|
||||
reset_button.set_label('重置滑块范围')
|
||||
reset_button.clicked.connect(self.core.functions.reset_control_sliders)
|
||||
|
||||
def twist_graph_pop_out(self):
|
||||
"""扭曲曲线大图弹出窗口"""
|
||||
name = WINDOWS.TwistGraphWindow.name
|
||||
if mc.workspaceControl(name, q=1, ex=1):
|
||||
mc.deleteUI(name)
|
||||
pop_out = PopOutWindow(name, '扭曲曲线大图', 512, 400)
|
||||
graph = FallOffCurve(pop_out.widget_layout, 'twistCurve_large')
|
||||
|
||||
def twist_graph_command(_):
|
||||
self.core.attributes.propagate_graphs(graph)
|
||||
self.core.attributes.store_graphs(graph)
|
||||
graph.change_command(twist_graph_command)
|
||||
with Row(pop_out.widget_layout, margins=style.scale([5, 0, 5, 2])) as row:
|
||||
row.setFixedHeight(int(style.BUTTON_HEIGHT))
|
||||
magnitude = FloatField('Magnitude_large', row.layout(), attr_name='Magnitude')
|
||||
magnitude.setFixedWidth(style.scale(45))
|
||||
magnitude.set_range(-99, 99)
|
||||
magnitude.set_step(0.01)
|
||||
magnitude.set_precision(2)
|
||||
magnitude.set_drag_command(partial(self.core.sliders.curve_control_slider_drag, magnitude))
|
||||
magnitude.set_release_command(self.core.sliders.release)
|
||||
reset_button = Button(row.layout())
|
||||
reset_button.set_label('重置曲线')
|
||||
|
||||
def reset_twist():
|
||||
self.core.utils.reset_single_graph('twist')
|
||||
self.core.attributes.store_graphs(graph)
|
||||
reset_button.clicked.connect(undo(reset_twist))
|
||||
self.core.curve_control.update_ui()
|
||||
|
||||
def width_graph_pop_out(self):
|
||||
"""宽度曲线大图弹出窗口"""
|
||||
name = WINDOWS.WidthGraphWindow.name
|
||||
if mc.workspaceControl(name, q=1, ex=1):
|
||||
mc.deleteUI(name)
|
||||
pop_out = PopOutWindow(name, '宽度曲线大图', 512, 400)
|
||||
graph = FallOffCurve(pop_out.widget_layout, 'scaleCurve_large')
|
||||
|
||||
def width_graph_command(_):
|
||||
self.core.attributes.propagate_graphs(graph)
|
||||
self.core.attributes.store_graphs(graph)
|
||||
graph.change_command(width_graph_command)
|
||||
with Row(pop_out.widget_layout, margins=style.scale([5, 0, 5, 2])) as row:
|
||||
row.setFixedHeight(int(style.BUTTON_HEIGHT))
|
||||
reset_button = Button(row.layout())
|
||||
reset_button.set_label('重置曲线')
|
||||
|
||||
def reset_width_cmd():
|
||||
self.core.utils.reset_single_graph('width')
|
||||
self.core.attributes.store_graphs(graph)
|
||||
reset_button.clicked.connect(undo(reset_width_cmd))
|
||||
self.core.curve_control.update_ui()
|
||||
|
||||
def profile_graph_pop_out(self):
|
||||
"""轮廓曲线大图弹出窗口"""
|
||||
name = WINDOWS.ProfileGraphWindow.name
|
||||
if mc.workspaceControl(name, q=1, ex=1):
|
||||
mc.deleteUI(name)
|
||||
pop_out = PopOutWindow(name, '轮廓曲线大图', 512, 400)
|
||||
graph = FallOffCurve(pop_out.widget_layout, 'profileCurve_large', attr=False)
|
||||
|
||||
def change_command(value):
|
||||
self.core.attributes.update_lattice(value)
|
||||
self.core.attributes.equalize_profile_curve()
|
||||
graph.change_command(change_command)
|
||||
with Row(pop_out.widget_layout, margins=style.scale([5, 0, 5, 2])) as row:
|
||||
row.setFixedHeight(int(style.BUTTON_HEIGHT))
|
||||
reset_button = Button(row.layout())
|
||||
reset_button.set_label('重置曲线')
|
||||
reset_button.clicked.connect(undo(self.core.attributes.reset_profile_curve))
|
||||
self.core.curve_control.update_ui()
|
||||
@@ -0,0 +1,308 @@
|
||||
"""
|
||||
几何体转曲线弹出窗口
|
||||
---
|
||||
GS CurveTools 许可证:
|
||||
名为 GS CurveTools 的此代码集合归 George Sladkovsky(Yehor Sladkovskyi)所有,
|
||||
未经其书面许可,不得复制或分发。
|
||||
|
||||
GS CurveTools v1.3.10 个人版
|
||||
版权所有 2025,George Sladkovsky(Yehor Sladkovskyi)
|
||||
保留所有权利
|
||||
|
||||
UI 字体为 Roboto,遵循 Apache 2.0 许可证:
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Autodesk Maya 是 Autodesk, Inc 的产品:
|
||||
https://www.autodesk.com/
|
||||
|
||||
社交媒体和联系方式:
|
||||
|
||||
Discord 服务器: https://discord.gg/f4DH6HQ
|
||||
在线商店: https://sladkovsky3d.artstation.com/store
|
||||
在线文档: https://gs-curvetools.readthedocs.io/
|
||||
Twitch 频道: https://www.twitch.tv/videonomad
|
||||
YouTube 频道: https://www.youtube.com/c/GeorgeSladkovsky
|
||||
ArtStation 作品集: https://www.artstation.com/sladkovsky3d
|
||||
联系邮箱: george.sladkovsky@gmail.com
|
||||
"""
|
||||
from ast import literal_eval
|
||||
import maya.cmds as mc
|
||||
from gs_curvetools.api.maya_tools import undo
|
||||
from gs_curvetools.api.qt_compat import *
|
||||
from gs_curvetools.config.constants import *
|
||||
from gs_curvetools.core.core import Core
|
||||
from gs_curvetools.debug.logger import logger
|
||||
from gs_curvetools.managers.options_manager import OptionsManager
|
||||
from gs_curvetools.managers.widget_manager import WidgetManager
|
||||
from gs_curvetools.ui import style
|
||||
from gs_curvetools.ui.widgets import Button, Column, Frame, LineEdit, PopOutWindow, Row, separator
|
||||
|
||||
class GeoToCurveWindow(QWidget):
|
||||
"""创建一个用于几何体转曲线的弹出窗口"""
|
||||
|
||||
def __init__(self, parent):
|
||||
super(GeoToCurveWindow, self).__init__(parent)
|
||||
self.ui_name = WINDOWS.GeoToCurveWindow.name
|
||||
self.core = Core.singleton()
|
||||
self.widget_manager = WidgetManager()
|
||||
self.options_manager = OptionsManager()
|
||||
self.buttons_dict = literal_eval(self.options_manager.get('CardToCurveOptions'))
|
||||
|
||||
def get_button(self, name):
|
||||
"""按名称获取按钮"""
|
||||
return self.buttons_dict[name] if name in self.buttons_dict else True
|
||||
|
||||
def open_ui(self):
|
||||
"""创建一个用于几何体转曲线的弹出窗口"""
|
||||
if mc.workspaceControl(self.ui_name, q=1, ex=1):
|
||||
if mc.workspaceControl(self.ui_name, q=1, vis=1):
|
||||
mc.deleteUI(self.ui_name)
|
||||
return
|
||||
mc.deleteUI(self.ui_name)
|
||||
self.pop_out = PopOutWindow(self.ui_name, '几何体转曲线', 270, 365)
|
||||
layout = self.pop_out.widget_layout
|
||||
layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
|
||||
self.main_button_group = QtWidgets.QButtonGroup(self.pop_out)
|
||||
self.main_button_group.setExclusive(False)
|
||||
self.main_button_group.buttonClicked.connect(self.save_buttons_state)
|
||||
with Column(layout) as main_column:
|
||||
main_column_layout = main_column.layout()
|
||||
if not isinstance(main_column_layout, QVBoxLayout):
|
||||
raise RuntimeError('主列布局未找到')
|
||||
main_column_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
|
||||
with Frame(main_column.layout(), label='输出类型:', obj_name='gsGeoToCurve_outputTypeSwitch') as output_type_frame:
|
||||
output_type_frame.set_collapsible(False)
|
||||
output_type_frame.set_collapsed(False)
|
||||
with Row(output_type_frame.get_frame_layout()) as output_type_switch:
|
||||
self.output_type_group = QtWidgets.QButtonGroup(self.pop_out)
|
||||
self.widget_manager.add('gsGeoToCurve_cardTypeGroup', self.output_type_group)
|
||||
generate_auto = Button(output_type_switch.layout(), 'gsGeoToCurve_generateAuto')
|
||||
generate_auto.set_button_style('small')
|
||||
generate_auto.set_label('自动')
|
||||
generate_auto.setCheckable(True)
|
||||
generate_cards = Button(output_type_switch.layout(), 'gsGeoToCurve_generateCards')
|
||||
generate_cards.set_button_style('small')
|
||||
generate_cards.set_label('卡片')
|
||||
generate_cards.setCheckable(True)
|
||||
generate_tubes = Button(output_type_switch.layout(), 'gsGeoToCurve_generateTubes')
|
||||
generate_tubes.set_button_style('small')
|
||||
generate_tubes.set_label('管道')
|
||||
generate_tubes.setCheckable(True)
|
||||
generate_curves = Button(output_type_switch.layout(), 'gsGeoToCurve_generateCurves')
|
||||
generate_curves.set_button_style('small')
|
||||
generate_curves.set_label('曲线')
|
||||
generate_curves.setCheckable(True)
|
||||
self.output_type_group.addButton(generate_auto, 0)
|
||||
self.output_type_group.addButton(generate_cards, 1)
|
||||
self.output_type_group.addButton(generate_tubes, 2)
|
||||
self.output_type_group.addButton(generate_curves, 3)
|
||||
checked_id = self.options_manager.get('CardToCurveOutputType')
|
||||
generate_auto.setChecked(checked_id == 0)
|
||||
generate_cards.setChecked(checked_id == 1)
|
||||
generate_tubes.setChecked(checked_id == 2)
|
||||
generate_curves.setChecked(checked_id == 3)
|
||||
self.output_type_group.buttonClicked.connect(self.update_active_buttons)
|
||||
with Frame(main_column.layout(), label='对象类型:', obj_name='gsGeoToCurve_cardType') as card_type_frame:
|
||||
self.card_type_frame = card_type_frame
|
||||
card_type_frame.set_collapsible(False)
|
||||
card_type_frame.set_collapsed(False)
|
||||
with Row(card_type_frame.get_frame_layout()) as output_type_switch:
|
||||
self.card_type_group = QtWidgets.QButtonGroup(output_type_switch)
|
||||
self.widget_manager.add('gsGeoToCurve_cardTypeGroup', self.card_type_group)
|
||||
self.warp_cards = Button(output_type_switch.layout(), 'gsGeoToCurve_warp')
|
||||
self.warp_cards.set_button_style('small')
|
||||
self.warp_cards.set_label('变形')
|
||||
self.warp_cards.setCheckable(True)
|
||||
extrude_cards = Button(output_type_switch.layout(), 'gsGeoToCurve_extrude')
|
||||
extrude_cards.set_button_style('small')
|
||||
extrude_cards.set_label('挤出')
|
||||
extrude_cards.setCheckable(True)
|
||||
self.card_type_group.addButton(self.warp_cards, 0)
|
||||
self.card_type_group.addButton(extrude_cards, 1)
|
||||
self.warp_cards.setChecked(not self.options_manager.get('CardToCurveCardType'))
|
||||
extrude_cards.setChecked(self.options_manager.get('CardToCurveCardType'))
|
||||
self.card_type_group.buttonClicked.connect(self.update_active_buttons)
|
||||
with Frame(main_column.layout(), label='匹配属性:', obj_name='gsGeoToCurve_matchAttributes') as match_attribute_frame:
|
||||
self.match_attribute_frame = match_attribute_frame
|
||||
match_attribute_frame.set_collapsible(False)
|
||||
match_attribute_frame.set_collapsed(False)
|
||||
with Row(match_attribute_frame.get_frame_layout()) as row:
|
||||
orientation = Button(row.layout(), 'gsGeoToCurve_orientation')
|
||||
orientation.set_button_style('small')
|
||||
orientation.set_label('方向')
|
||||
orientation.setCheckable(True)
|
||||
orientation.setChecked(bool(self.get_button('gsGeoToCurve_orientation')))
|
||||
width = Button(row.layout(), 'gsGeoToCurve_width')
|
||||
width.set_button_style('small')
|
||||
width.set_label('宽度')
|
||||
width.setCheckable(True)
|
||||
width.setChecked(bool(self.get_button('gsGeoToCurve_width')))
|
||||
self.main_button_group.addButton(orientation)
|
||||
self.main_button_group.addButton(width)
|
||||
with Row(match_attribute_frame.get_frame_layout()) as row:
|
||||
taper = Button(row.layout(), 'gsGeoToCurve_taper')
|
||||
taper.set_button_style('small')
|
||||
taper.set_label('锥度')
|
||||
taper.setCheckable(True)
|
||||
taper.setChecked(bool(self.get_button('gsGeoToCurve_taper')))
|
||||
twist = Button(row.layout(), 'gsGeoToCurve_twist')
|
||||
twist.set_button_style('small')
|
||||
twist.set_label('扭曲')
|
||||
twist.setCheckable(True)
|
||||
twist.setChecked(bool(self.get_button('gsGeoToCurve_twist')))
|
||||
self.main_button_group.addButton(taper)
|
||||
self.main_button_group.addButton(twist)
|
||||
with Row(match_attribute_frame.get_frame_layout()) as row:
|
||||
profile = Button(row.layout(), 'gsGeoToCurve_profile')
|
||||
profile.set_button_style('small')
|
||||
profile.set_label('轮廓')
|
||||
profile.setCheckable(True)
|
||||
profile.setChecked(bool(self.get_button('gsGeoToCurve_profile')))
|
||||
material = Button(row.layout(), 'gsGeoToCurve_material')
|
||||
material.set_button_style('small')
|
||||
material.set_label('材质')
|
||||
material.setCheckable(True)
|
||||
material.setChecked(bool(self.get_button('gsGeoToCurve_material')))
|
||||
self.main_button_group.addButton(profile)
|
||||
self.main_button_group.addButton(material)
|
||||
uvs = Button(match_attribute_frame.get_frame_layout(), 'gsGeoToCurve_UVs')
|
||||
uvs.set_button_style('small')
|
||||
uvs.set_label('UVs')
|
||||
uvs.setCheckable(True)
|
||||
uvs.setChecked(bool(self.get_button('gsGeoToCurve_UVs')))
|
||||
uvs.clicked.connect(self.update_active_buttons)
|
||||
self.main_button_group.addButton(uvs)
|
||||
with Frame(main_column.layout(), label='UV 匹配选项:', obj_name='gsGeoToCurve_UVMatchOptions') as uv_match_options_frame:
|
||||
self.uv_match_options_frame = uv_match_options_frame
|
||||
uv_match_options_frame.set_collapsible(False)
|
||||
uv_match_options_frame.set_collapsed(False)
|
||||
with Row(uv_match_options_frame.get_frame_layout()) as row:
|
||||
vertical_flip = Button(row.layout(), 'gsGeoToCurve_verticalFlip')
|
||||
vertical_flip.set_button_style('small')
|
||||
vertical_flip.set_label('垂直翻转')
|
||||
vertical_flip.setCheckable(True)
|
||||
vertical_flip.setChecked(bool(self.get_button('gsGeoToCurve_verticalFlip')))
|
||||
horizontal_flip = Button(row.layout(), 'gsGeoToCurve_horizontalFlip')
|
||||
horizontal_flip.set_button_style('small')
|
||||
horizontal_flip.set_label('水平翻转')
|
||||
horizontal_flip.setCheckable(True)
|
||||
horizontal_flip.setChecked(bool(self.get_button('gsGeoToCurve_horizontalFlip')))
|
||||
self.main_button_group.addButton(vertical_flip)
|
||||
self.main_button_group.addButton(horizontal_flip)
|
||||
with Frame(main_column.layout(), label='其他:') as other_options_frame:
|
||||
other_options_frame.set_collapsible(False)
|
||||
other_options_frame.set_collapsed(False)
|
||||
reverse_curve = Button(other_options_frame.get_frame_layout(), 'gsGeoToCurve_reverseCurve')
|
||||
reverse_curve.set_button_style('small')
|
||||
reverse_curve.set_label('反转曲线')
|
||||
reverse_curve.setCheckable(True)
|
||||
reverse_curve.setChecked(bool(self.get_button('gsGeoToCurve_reverseCurve')))
|
||||
self.main_button_group.addButton(reverse_curve)
|
||||
delete_original_object = Button(other_options_frame.get_frame_layout(), 'gsGeoToCurve_deleteOriginalObject')
|
||||
delete_original_object.set_button_style('small')
|
||||
delete_original_object.set_label('删除原始对象')
|
||||
delete_original_object.setCheckable(True)
|
||||
delete_original_object.setChecked(bool(self.get_button('gsGeoToCurve_deleteOriginalObject')))
|
||||
self.main_button_group.addButton(delete_original_object)
|
||||
use_aim_mesh = Button(other_options_frame.get_frame_layout(), 'gsGeoToCurve_useAimMesh')
|
||||
use_aim_mesh.set_button_style('small')
|
||||
use_aim_mesh.set_label('使用目标网格')
|
||||
use_aim_mesh.setCheckable(True)
|
||||
use_aim_mesh.setChecked(bool(self.get_button('gsGeoToCurve_useAimMesh')))
|
||||
use_aim_mesh.clicked.connect(self.update_active_buttons)
|
||||
self.main_button_group.addButton(use_aim_mesh)
|
||||
with Row(other_options_frame.get_frame_layout(), spacing=style.scale(4)) as aim_mesh_row:
|
||||
select_aim_mesh = Button(aim_mesh_row.layout(), 'gsGeoToCurve_selectAimMesh')
|
||||
select_aim_mesh.setFixedWidth(style.scale(100))
|
||||
select_aim_mesh.set_label('选择目标网格')
|
||||
select_aim_mesh.clicked.connect(self.select_aim_mesh_from_scene)
|
||||
aim_mesh_name = LineEdit('gsGeoToCurve_aimMeshName', aim_mesh_row.layout())
|
||||
aim_mesh_name.setPlaceholderText('选择或输入目标网格名称')
|
||||
aim_mesh_name.setClearButtonEnabled(True)
|
||||
aim_mesh_name.editingFinished.connect(self.save_buttons_state)
|
||||
aim_mesh_name.setText(str(self.get_button('gsGeoToCurve_aimMeshName')))
|
||||
main_column_layout.addWidget(separator())
|
||||
with Row(main_column.layout(), margins=style.scale([0, 3, 0, 3])) as _:
|
||||
pass
|
||||
main_column_layout.addWidget(separator())
|
||||
with Row(main_column.layout()) as row:
|
||||
convert_selected = Button(row.layout(), obj_name='gsGeoToCurve_convertSelected')
|
||||
convert_selected.set_label('转换选中项')
|
||||
convert_selected.clicked.connect(undo(self.core.conversion.geo_to_curve))
|
||||
cancel = Button(row.layout())
|
||||
cancel.set_label('关闭并保存')
|
||||
cancel.clicked.connect(self.close_ui)
|
||||
self.update_active_buttons()
|
||||
self.save_buttons_state()
|
||||
|
||||
def close_ui(self):
|
||||
"""关闭弹出窗口"""
|
||||
self.save_buttons_state()
|
||||
if mc.workspaceControl(self.ui_name, q=1, ex=1):
|
||||
if mc.workspaceControl(self.ui_name, q=1, vis=1):
|
||||
mc.deleteUI(self.ui_name)
|
||||
else:
|
||||
mc.deleteUI(self.ui_name)
|
||||
|
||||
def select_aim_mesh_from_scene(self):
|
||||
"""点击按钮时从场景中选择目标网格"""
|
||||
mesh = mc.ls(sl=1, tr=1)
|
||||
if not mesh:
|
||||
return
|
||||
for obj in mesh:
|
||||
if mc.nodeType(mc.listRelatives(obj, c=1, pa=1)) != 'mesh':
|
||||
continue
|
||||
self.widget_manager.get('gsGeoToCurve_aimMeshName').setText(obj)
|
||||
break
|
||||
else:
|
||||
logger.warning('未选择兼容的网格。请选择一个网格对象。', in_view=True)
|
||||
self.save_buttons_state()
|
||||
|
||||
def update_active_buttons(self):
|
||||
"""Updates the active buttons based on the output type"""
|
||||
checked_id = self.output_type_group.checkedId()
|
||||
self.options_manager.set('CardToCurveOutputType', checked_id)
|
||||
self.options_manager.set('CardToCurveCardType', int(not self.warp_cards.isChecked()))
|
||||
if checked_id == 0:
|
||||
self.match_attribute_frame.setEnabled(True)
|
||||
self.card_type_frame.setEnabled(True)
|
||||
if self.widget_manager.get('gsGeoToCurve_UVs').isChecked():
|
||||
self.uv_match_options_frame.setEnabled(True)
|
||||
else:
|
||||
self.uv_match_options_frame.setEnabled(False)
|
||||
elif checked_id == 1:
|
||||
self.match_attribute_frame.setEnabled(True)
|
||||
self.card_type_frame.setEnabled(True)
|
||||
if self.widget_manager.get('gsGeoToCurve_UVs').isChecked():
|
||||
self.uv_match_options_frame.setEnabled(True)
|
||||
else:
|
||||
self.uv_match_options_frame.setEnabled(False)
|
||||
elif checked_id == 2:
|
||||
self.match_attribute_frame.setEnabled(True)
|
||||
self.card_type_frame.setEnabled(True)
|
||||
if self.widget_manager.get('gsGeoToCurve_UVs').isChecked():
|
||||
self.uv_match_options_frame.setEnabled(True)
|
||||
else:
|
||||
self.uv_match_options_frame.setEnabled(False)
|
||||
elif checked_id == 3:
|
||||
self.match_attribute_frame.setEnabled(False)
|
||||
self.uv_match_options_frame.setEnabled(False)
|
||||
self.card_type_frame.setEnabled(False)
|
||||
is_aim_checked = self.widget_manager.get('gsGeoToCurve_useAimMesh').isChecked()
|
||||
if is_aim_checked:
|
||||
self.widget_manager.get('gsGeoToCurve_aimMeshName').setEnabled(True)
|
||||
self.widget_manager.get('gsGeoToCurve_selectAimMesh').setEnabled(True)
|
||||
else:
|
||||
self.widget_manager.get('gsGeoToCurve_aimMeshName').setEnabled(False)
|
||||
self.widget_manager.get('gsGeoToCurve_selectAimMesh').setEnabled(False)
|
||||
|
||||
def save_buttons_state(self):
|
||||
"""Saves the buttons state"""
|
||||
buttons = self.main_button_group.buttons()
|
||||
buttons_dict = {}
|
||||
for b in buttons:
|
||||
assert isinstance(b, Button), 'Button {b} is not a Button object'.format(b=b)
|
||||
buttons_dict.update({b.obj_name: b.isChecked()})
|
||||
else:
|
||||
buttons_dict.update({'gsGeoToCurve_aimMeshName': self.widget_manager.get('gsGeoToCurve_aimMeshName').text()})
|
||||
self.options_manager.set('CardToCurveOptions', str(buttons_dict))
|
||||
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
随机化曲线窗口
|
||||
---
|
||||
GS曲线工具许可协议:
|
||||
名为GS曲线工具的此代码集合归乔治·斯拉德科夫斯基(叶戈尔·斯拉德科夫斯基)所有,
|
||||
未经他的书面许可,不得复制或分发。
|
||||
|
||||
GS曲线工具 v1.3.10 个人版
|
||||
版权所有 2025,乔治·斯拉德科夫斯基(叶戈尔·斯拉德科夫斯基)
|
||||
保留所有权利
|
||||
|
||||
用户界面字体为Roboto,该字体根据Apache 2.0许可协议授权:
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Autodesk Maya是Autodesk公司的产品:
|
||||
https://www.autodesk.com/
|
||||
|
||||
社交媒体和联系方式:
|
||||
|
||||
Discord服务器: https://discord.gg/f4DH6HQ
|
||||
在线商店: https://sladkovsky3d.artstation.com/store
|
||||
在线文档: https://gs-curvetools.readthedocs.io/
|
||||
Twitch频道: https://www.twitch.tv/videonomad
|
||||
YouTube频道: https://www.youtube.com/c/GeorgeSladkovsky
|
||||
ArtStation作品集: https://www.artstation.com/sladkovsky3d
|
||||
联系邮箱: george.sladkovsky@gmail.com
|
||||
"""
|
||||
from functools import partial
|
||||
import maya.cmds as mc
|
||||
from gs_curvetools.api.qt_compat import *
|
||||
from gs_curvetools.config.constants import *
|
||||
from gs_curvetools.core.core import Core
|
||||
from gs_curvetools.ui.widgets import Button, Frame, MayaSlider, PopOutWindow, Row, maya_slider, separator
|
||||
|
||||
|
||||
class RandomizeCurveWindow(QWidget):
|
||||
"""创建一个用于随机化曲线的弹出窗口"""
|
||||
|
||||
def __init__(self, parent):
|
||||
"""创建一个用于随机化曲线的弹出窗口"""
|
||||
super(RandomizeCurveWindow, self).__init__(parent)
|
||||
self.core = Core.singleton()
|
||||
|
||||
def open_ui(self):
|
||||
window_name = WINDOWS.RandomizeCurveWindow.name
|
||||
if mc.workspaceControl(window_name, q=1, ex=1):
|
||||
if mc.workspaceControl(window_name, q=1, vis=1):
|
||||
mc.deleteUI(window_name)
|
||||
return
|
||||
mc.deleteUI(window_name)
|
||||
pop_out = PopOutWindow(window_name, '随机化曲线', 270, 565)
|
||||
layout = pop_out.widget_layout
|
||||
layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
def release(slider, *_):
|
||||
self.core.sliders.rand_slider_drag(slider)
|
||||
self.core.sliders.rand_slider_release(slider)
|
||||
self.core.sliders.release()
|
||||
|
||||
def create_enable_slider_row(frame_layout, button_name, slider_name, label):
|
||||
with Row(frame_layout) as row:
|
||||
enable = Button(row.layout(), button_name)
|
||||
enable.set_label('启用', line_height=100)
|
||||
enable.set_button_style('small')
|
||||
enable.setCheckable(True)
|
||||
slider = maya_slider(mc.floatSliderGrp(slider_name, l='倍数:', min=1, max=50, pre=1, step=0.5, cw=[(1, 30), (2, 1)]))
|
||||
row_layout = row.layout()
|
||||
assert row_layout is not None
|
||||
row_layout.addWidget(slider)
|
||||
|
||||
with Frame(layout, label='控制点') as frame:
|
||||
frame.set_collapsible(False)
|
||||
create_enable_slider_row(frame.get_frame_layout(), 'curveRandomizeSlider0', 'gsCurveCVsRandMulti', '控制点')
|
||||
with Row(frame.get_frame_layout()) as row:
|
||||
lock_first = Button(row.layout(), 'gsLockFirstCV')
|
||||
lock_first.set_label('锁定第一个控制点', line_height=100)
|
||||
lock_first.set_button_style('small')
|
||||
lock_first.setCheckable(True)
|
||||
lock_last = Button(row.layout(), 'gsLockLastCV')
|
||||
lock_last.set_label('锁定最后一个控制点', line_height=100)
|
||||
lock_last.set_button_style('small')
|
||||
lock_last.setCheckable(True)
|
||||
with Row(frame.get_frame_layout()) as row:
|
||||
axis_x = Button(row.layout(), 'gsRandAxisX')
|
||||
axis_x.set_label('X', line_height=100)
|
||||
axis_x.set_button_style('small')
|
||||
axis_x.setCheckable(True)
|
||||
axis_y = Button(row.layout(), 'gsRandAxisY')
|
||||
axis_y.set_label('Y', line_height=100)
|
||||
axis_y.set_button_style('small')
|
||||
axis_y.setCheckable(True)
|
||||
axis_z = Button(row.layout(), 'gsRandAxisZ')
|
||||
axis_z.set_label('Z', line_height=100)
|
||||
axis_z.set_button_style('small')
|
||||
axis_z.setCheckable(True)
|
||||
MayaSlider(mc.floatSliderGrp('gsCurveCVsRand', min=0, max=1, step=0.05, dc=partial(self.core.sliders.rand_slider_drag, 0), cc=partial(release, 0)), layout=frame.get_frame_layout())
|
||||
|
||||
with Frame(layout, label='旋转') as frame:
|
||||
frame.set_collapsible(False)
|
||||
create_enable_slider_row(frame.get_frame_layout(), 'curveRandomizeSlider1', 'gsCurveRotationRandMulti', '旋转')
|
||||
with Row(frame.get_frame_layout()) as row:
|
||||
axis_x = Button(row.layout(), 'gsRandRotateAxisX')
|
||||
axis_x.set_label('X', line_height=100)
|
||||
axis_x.set_button_style('small')
|
||||
axis_x.setCheckable(True)
|
||||
axis_y = Button(row.layout(), 'gsRandRotateAxisY')
|
||||
axis_y.set_label('Y', line_height=100)
|
||||
axis_y.set_button_style('small')
|
||||
axis_y.setCheckable(True)
|
||||
axis_z = Button(row.layout(), 'gsRandRotateAxisZ')
|
||||
axis_z.set_label('Z', line_height=100)
|
||||
axis_z.set_button_style('small')
|
||||
axis_z.setCheckable(True)
|
||||
MayaSlider(mc.floatSliderGrp('gsCurveRotationRand', min=0, max=1, step=0.05, dc=partial(self.core.sliders.rand_slider_drag, 1), cc=partial(release, 1)), layout=frame.get_frame_layout())
|
||||
|
||||
with Frame(layout, label='方向') as frame:
|
||||
frame.set_collapsible(False)
|
||||
create_enable_slider_row(frame.get_frame_layout(), 'curveRandomizeSlider2', 'gsCurveOrientationRandMulti', '方向')
|
||||
MayaSlider(mc.floatSliderGrp('gsCurveOrientationRand', min=0, max=1, step=0.05, dc=partial(self.core.sliders.rand_slider_drag, 2), cc=partial(release, 2)), layout=frame.get_frame_layout())
|
||||
|
||||
with Frame(layout, label='扭曲') as frame:
|
||||
frame.set_collapsible(False)
|
||||
create_enable_slider_row(frame.get_frame_layout(), 'curveRandomizeSlider3', 'gsCurveTwistRandMulti', '扭曲')
|
||||
MayaSlider(mc.floatSliderGrp('gsCurveTwistRand', min=0, max=1, step=0.05, dc=partial(self.core.sliders.rand_slider_drag, 3), cc=partial(release, 3)), layout=frame.get_frame_layout())
|
||||
|
||||
with Frame(layout, label='宽度') as frame:
|
||||
frame.set_collapsible(False)
|
||||
create_enable_slider_row(frame.get_frame_layout(), 'curveRandomizeSlider4', 'gsCurveWidthRandMulti', '宽度')
|
||||
uniform = Button(frame.get_frame_layout(), 'gsWidthCheckBoxUniform')
|
||||
uniform.set_button_style('small')
|
||||
uniform.set_label('均匀', line_height=100)
|
||||
uniform.setCheckable(True)
|
||||
MayaSlider(mc.floatSliderGrp('gsCurveWidthRand', min=0.001, max=1, step=0.05, dc=partial(self.core.sliders.rand_slider_drag, 4), cc=partial(release, 4)), layout=frame.get_frame_layout())
|
||||
|
||||
with Frame(layout, label='锥度') as frame:
|
||||
frame.set_collapsible(False)
|
||||
create_enable_slider_row(frame.get_frame_layout(), 'curveRandomizeSlider5', 'gsCurveTaperRandMulti', '锥度')
|
||||
MayaSlider(mc.floatSliderGrp('gsCurveTaperRand', min=0, max=1, step=0.05, dc=partial(self.core.sliders.rand_slider_drag, 5), cc=partial(release, 5)), layout=frame.get_frame_layout())
|
||||
|
||||
with Frame(layout, label='轮廓') as frame:
|
||||
frame.set_collapsible(False)
|
||||
create_enable_slider_row(frame.get_frame_layout(), 'curveRandomizeSlider6', 'gsCurveProfileRandMulti', '轮廓')
|
||||
uniform = Button(frame.get_frame_layout(), 'gsProfileCheckBoxNegative')
|
||||
uniform.set_button_style('small')
|
||||
uniform.set_label('允许负值', line_height=100)
|
||||
uniform.setCheckable(True)
|
||||
MayaSlider(mc.floatSliderGrp('gsCurveProfileRand', min=0, max=1, step=0.05, dc=partial(self.core.sliders.rand_slider_drag, 6), cc=partial(release, 6)), layout=frame.get_frame_layout())
|
||||
|
||||
with Frame(layout, label='选择') as frame:
|
||||
frame.set_collapsible(False)
|
||||
enable = Button(frame.get_frame_layout(), 'curveRandomizeSlider7')
|
||||
enable.set_label('启用', line_height=100)
|
||||
enable.set_button_style('small')
|
||||
enable.setCheckable(True)
|
||||
MayaSlider(mc.floatSliderGrp('gsCurveSelectRand', min=0, max=1, step=0.05, dc=partial(self.core.sliders.rand_slider_drag, 7), cc=partial(release, 7)), layout=frame.get_frame_layout())
|
||||
|
||||
layout.addWidget(separator())
|
||||
with Row(layout) as row:
|
||||
|
||||
def randomize_click():
|
||||
self.core.sliders.rand_slider_drag(-1)
|
||||
self.core.sliders.rand_slider_release(-1)
|
||||
self.core.sliders.release()
|
||||
|
||||
randomize = Button(row.layout())
|
||||
randomize.set_label('随机化')
|
||||
randomize.clicked.connect(randomize_click)
|
||||
close = Button(row.layout())
|
||||
close.set_label('关闭')
|
||||
close.clicked.connect(lambda: mc.deleteUI(window_name))
|
||||
@@ -0,0 +1,636 @@
|
||||
"""
|
||||
UV 编辑器窗口
|
||||
---
|
||||
GS CurveTools 许可证:
|
||||
名为 GS CurveTools 的此代码集合归 George Sladkovsky(Yehor Sladkovskyi)所有,
|
||||
未经其书面许可,不得复制或分发。
|
||||
|
||||
GS CurveTools v1.3.10 个人版
|
||||
版权所有 2025,George Sladkovsky(Yehor Sladkovskyi)
|
||||
保留所有权利
|
||||
|
||||
UI 字体为 Roboto,遵循 Apache 2.0 许可证:
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Autodesk Maya 是 Autodesk, Inc 的产品:
|
||||
https://www.autodesk.com/
|
||||
|
||||
社交媒体和联系方式:
|
||||
|
||||
Discord 服务器: https://discord.gg/f4DH6HQ
|
||||
在线商店: https://sladkovsky3d.artstation.com/store
|
||||
在线文档: https://gs-curvetools.readthedocs.io/
|
||||
Twitch 频道: https://www.twitch.tv/videonomad
|
||||
YouTube 频道: https://www.youtube.com/c/GeorgeSladkovsky
|
||||
ArtStation 作品集: https://www.artstation.com/sladkovsky3d
|
||||
联系邮箱: george.sladkovsky@gmail.com
|
||||
"""
|
||||
import os
|
||||
from ast import literal_eval
|
||||
import maya.cmds as mc
|
||||
from gs_curvetools.api.maya_tools import deferred_lp, get_mod, no_undo, undo
|
||||
from gs_curvetools.api.python import Timer
|
||||
from gs_curvetools.api.qt_compat import *
|
||||
from gs_curvetools.api.utils import clr_to_float, open_link
|
||||
from gs_curvetools.config.constants import *
|
||||
from gs_curvetools.core.core import Core
|
||||
from gs_curvetools.debug.logger import logger
|
||||
from gs_curvetools.managers.options_manager import OptionsManager
|
||||
from gs_curvetools.managers.script_jobs import ScriptJobs
|
||||
from gs_curvetools.managers.widget_manager import WidgetManager
|
||||
from gs_curvetools.ui import style
|
||||
from gs_curvetools.ui.components.uv_editor.editor import UVEditor
|
||||
from gs_curvetools.ui.utils import maya_dockable_window
|
||||
from gs_curvetools.ui.widgets import Button, ColorPicker, Column, Frame, Label, Row, UVItemList, separator
|
||||
|
||||
class UVEditorWindow(QWidget):
|
||||
"""UV 编辑器窗口"""
|
||||
|
||||
def __init__(self, parent):
|
||||
super(UVEditorWindow, self).__init__(parent)
|
||||
self.core = Core.singleton()
|
||||
self.widget_manager = WidgetManager()
|
||||
self.options_manager = OptionsManager()
|
||||
self.script_jobs = ScriptJobs.singleton()
|
||||
self.timer = Timer()
|
||||
self.mouse_toggle = False
|
||||
self.previous_selection = []
|
||||
self.uv_update_check = 0
|
||||
self.isolate_mode = False
|
||||
self.current_selection = []
|
||||
self.controller_group = None
|
||||
self.direction_switch = None
|
||||
|
||||
def open_ui(self):
|
||||
"""UV 编辑器工作区控件"""
|
||||
if mc.workspaceControl(WINDOWS.UVEditor.name, q=1, ex=1):
|
||||
if not mc.workspaceControl(WINDOWS.UVEditor.name, q=1, vis=1):
|
||||
mc.workspaceControl(WINDOWS.UVEditor.name, e=1, rs=1)
|
||||
else:
|
||||
mc.workspaceControl(WINDOWS.UVEditor.name, e=1, vis=0)
|
||||
try:
|
||||
self.update_editor()
|
||||
return
|
||||
except BaseException as exc:
|
||||
logger.exception(exc)
|
||||
return None
|
||||
self.init()
|
||||
self.update_editor()
|
||||
self.script_jobs.check_script_jobs(WINDOWS.UVEditor.name)
|
||||
|
||||
def init(self):
|
||||
"""初始化 UV 编辑器窗口"""
|
||||
if mc.workspaceControl(WINDOWS.UVEditor.name, q=1, ex=1):
|
||||
mc.deleteUI(WINDOWS.UVEditor.name)
|
||||
parent = maya_dockable_window(name=WINDOWS.UVEditor.name, label=WINDOWS.UVEditor.label, i_w=705, i_h=575, width_property='free')
|
||||
self.editor = UVEditor(parent)
|
||||
self.editor.setParent(parent)
|
||||
parent_layout = parent.layout()
|
||||
assert parent_layout is not None
|
||||
parent_layout.addWidget(self)
|
||||
self.window()
|
||||
self.editor.signal_key_press.connect(self.update_buttons)
|
||||
self.editor.signal_mouse_move.connect(self._update_curves)
|
||||
self.editor.signal_mouse_release.connect(self._stop_curves_update)
|
||||
self.editor.signal_uv_draw_end.connect(self.manual_curve_update)
|
||||
self.editor.signal_function_key_press.connect(undo(self.function_switch))
|
||||
self.uv_list.signal_key_pressed.connect(self.update_visibility)
|
||||
|
||||
def window(self):
|
||||
"""UV 编辑器窗口"""
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(*style.scale([2, 2, 2, 2]))
|
||||
scroll_widget = QtWidgets.QWidget()
|
||||
layout = QtWidgets.QVBoxLayout(scroll_widget)
|
||||
scroll_area = QtWidgets.QScrollArea(self)
|
||||
scroll_area.setWidget(scroll_widget)
|
||||
main_layout.addWidget(scroll_area)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(style.scale(2))
|
||||
layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
|
||||
scroll_area.setFrameShape(QtWidgets.QFrame.Shape.NoFrame)
|
||||
scroll_area.setWidgetResizable(True)
|
||||
self.editor.init()
|
||||
self.update_colors()
|
||||
with Row(layout) as main_row:
|
||||
with Column(main_row.layout()) as main_column:
|
||||
main_column_layout = main_column.layout()
|
||||
assert main_column_layout is not None
|
||||
main_column_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
|
||||
main_column.setFixedWidth(style.scale(130))
|
||||
main_column_layout.addWidget(separator())
|
||||
controller_label = Label(main_column.layout())
|
||||
controller_label.set_label('控制模式')
|
||||
self.controller_group = QtWidgets.QButtonGroup(main_column)
|
||||
self.controller_group.buttonReleased.connect(self.update_controller_mode)
|
||||
with Row(main_column.layout()) as select_move:
|
||||
select = Button(select_move.layout(), 'gsUVSelect')
|
||||
select.setCheckable(True)
|
||||
select.setChecked(True)
|
||||
select.set_label('选择 (Q)')
|
||||
move = Button(select_move.layout(), 'gsUVMove')
|
||||
move.setCheckable(True)
|
||||
move.set_label('移动 (W)')
|
||||
with Row(main_column.layout()) as rotate_scale:
|
||||
rotate = Button(rotate_scale.layout(), 'gsUVRotate')
|
||||
rotate.setCheckable(True)
|
||||
rotate.set_label('旋转 (E)')
|
||||
scale = Button(rotate_scale.layout(), 'gsUVScale')
|
||||
scale.setCheckable(True)
|
||||
scale.set_label('缩放 (R)')
|
||||
with Row(main_column.layout()) as scale_row:
|
||||
self.direction_switch = QtWidgets.QButtonGroup(scale_row)
|
||||
self.direction_switch.buttonReleased.connect(self.update_controller_mode)
|
||||
horizontal = Button(scale_row.layout(), 'gsUVHorizontalScale')
|
||||
horizontal.set_label('水平 (H)')
|
||||
horizontal.set_button_style('small')
|
||||
horizontal.setCheckable(True)
|
||||
horizontal.setChecked(True)
|
||||
vertical = Button(scale_row.layout(), 'gsUVVerticalScale')
|
||||
vertical.set_button_style('small')
|
||||
vertical.setCheckable(True)
|
||||
vertical.set_label('垂直 (V)')
|
||||
self.direction_switch.addButton(horizontal, 0)
|
||||
self.direction_switch.addButton(vertical, 1)
|
||||
draw_uv = Button(main_column.layout(), 'gsDrawUVs')
|
||||
draw_uv.setCheckable(True)
|
||||
draw_uv.set_label('绘制 (D)')
|
||||
self.controller_group.addButton(select, 0)
|
||||
self.controller_group.addButton(move, 1)
|
||||
self.controller_group.addButton(rotate, 2)
|
||||
self.controller_group.addButton(scale, 3)
|
||||
self.controller_group.addButton(draw_uv, 4)
|
||||
main_column_layout.addWidget(separator())
|
||||
utility_label = Label(main_column.layout())
|
||||
utility_label.set_label('实用功能')
|
||||
with Row(main_column.layout()) as hv_flip_row:
|
||||
h_flip_uv = Button(hv_flip_row.layout(), 'gsHorizontalFlipUV')
|
||||
h_flip_uv.set_label('水平翻转 (H)')
|
||||
h_flip_uv.clicked.connect(undo(self.horizontal_flip_uv))
|
||||
v_flip_uv = Button(hv_flip_row.layout(), 'gsVerticalFlipUV')
|
||||
v_flip_uv.set_label('垂直翻转 (V)')
|
||||
v_flip_uv.clicked.connect(undo(self.vertical_flip_uv))
|
||||
reset_uv = Button(main_column.layout(), 'gsResetUVs')
|
||||
reset_uv.set_label('重置 UV (X)')
|
||||
reset_uv.clicked.connect(undo(self.reset_uvs))
|
||||
sync_selection = Button(main_column.layout(), 'gsSyncSelectionUVs')
|
||||
sync_selection.set_label('同步选择 (S)')
|
||||
sync_selection.clicked.connect(undo(self.sync_selection))
|
||||
randomize_uvs = Button(main_column.layout(), 'gsRandomizeUVs')
|
||||
randomize_uvs.set_label('随机化')
|
||||
randomize_uvs.set_icon('mod-bottom')
|
||||
randomize_uvs.clicked.connect(undo(self.randomize_uvs))
|
||||
main_column_layout.addWidget(separator())
|
||||
uv_list_label = Label(main_column.layout())
|
||||
uv_list_label.set_label('UV 列表')
|
||||
self.uv_list = UVItemList(main_column_layout)
|
||||
self.uv_list.setLayout(main_column_layout)
|
||||
self.uv_list.signal_mouse_released.connect(self.update_visibility)
|
||||
focus_view = Button(main_column.layout(), 'gsFocusUVs')
|
||||
focus_view.set_label('聚焦视图 (F)')
|
||||
focus_view.clicked.connect(self.editor.focus_view)
|
||||
with Row(main_column.layout()) as hide_isolate:
|
||||
isolate_select = Button(hide_isolate.layout(), 'gsUVIsolateSelect')
|
||||
isolate_select.set_label('隔离选择 (I)')
|
||||
isolate_select.clicked.connect(self.isolate_select)
|
||||
hide = Button(hide_isolate.layout(), 'gsUVHide')
|
||||
hide.set_label('隐藏 (O)')
|
||||
hide.clicked.connect(self.hide)
|
||||
show_all_button = Button(main_column.layout(), 'gsUVShowAll')
|
||||
show_all_button.set_label('显示全部 (A)')
|
||||
show_all_button.clicked.connect(self.show_all)
|
||||
main_column_layout.addWidget(separator())
|
||||
with Frame(main_column.layout(), label='选项') as options_frame:
|
||||
texture_functions_label = Label(options_frame.get_frame_layout())
|
||||
texture_functions_label.set_label('纹理控制:')
|
||||
transparency = Button(options_frame.get_frame_layout(), 'UVEditorTransparencyToggle')
|
||||
transparency.set_button_style('small')
|
||||
transparency.setCheckable(True)
|
||||
transparency.setChecked(self.options_manager.get('UVEditorTransparencyToggle'))
|
||||
transparency.set_label('透明度')
|
||||
alpha_only = Button(options_frame.get_frame_layout(), 'UVEditorAlphaOnlyToggle')
|
||||
alpha_only.set_button_style('small')
|
||||
alpha_only.setCheckable(True)
|
||||
alpha_only.setChecked(self.options_manager.get('UVEditorAlphaOnly'))
|
||||
alpha_only.set_label('仅显示 Alpha 通道')
|
||||
use_transforms = Button(options_frame.get_frame_layout(), 'UVEditorUseTransforms')
|
||||
use_transforms.set_button_style('small')
|
||||
use_transforms.setCheckable(True)
|
||||
use_transforms.setChecked(True)
|
||||
use_transforms.set_label('使用变换')
|
||||
use_transforms.clicked.connect(self.update_texture)
|
||||
|
||||
def toggle_alpha_command():
|
||||
if transparency.isChecked():
|
||||
self.editor.toggle_alpha(True)
|
||||
else:
|
||||
self.editor.toggle_alpha(False)
|
||||
alpha_only.setChecked(False)
|
||||
self.update_texture()
|
||||
self.options_manager.set('UVEditorTransparencyToggle', transparency.isChecked())
|
||||
transparency.clicked.connect(toggle_alpha_command)
|
||||
|
||||
def toggle_alpha_only_command():
|
||||
if alpha_only.isChecked():
|
||||
transparency.setChecked(True)
|
||||
else:
|
||||
transparency.setChecked(False)
|
||||
toggle_alpha_command()
|
||||
self.options_manager.set('UVEditorAlphaOnly', alpha_only.isChecked())
|
||||
alpha_only.clicked.connect(toggle_alpha_only_command)
|
||||
options_frame.get_frame_layout().addWidget(separator())
|
||||
texture_functions_label = Label(options_frame.get_frame_layout())
|
||||
texture_functions_label.set_label('编辑器颜色:')
|
||||
with Row(options_frame.get_frame_layout(), margins=style.scale([5, 0, 5, 5])) as color_row:
|
||||
background_color_picker = ColorPicker('UVEditorBGColorPicker', color_row.layout())
|
||||
background_color_picker.set_rgb_colors(clr_to_float(literal_eval(self.options_manager.get('UVEditorBGColor'))))
|
||||
background_color_picker.connect_command(self.update_colors)
|
||||
grid_color_picker = ColorPicker('UVEditorGridColorPicker', color_row.layout())
|
||||
grid_color_picker.set_rgb_colors(clr_to_float(literal_eval(self.options_manager.get('UVEditorGridColor'))))
|
||||
grid_color_picker.connect_command(self.update_colors)
|
||||
frame_color_picker = ColorPicker('UVEditorFrameColorPicker', color_row.layout())
|
||||
frame_color_picker.set_rgb_colors(clr_to_float(literal_eval(self.options_manager.get('UVEditorFrameColor'))))
|
||||
frame_color_picker.connect_command(self.update_colors)
|
||||
card_colors = Label(options_frame.get_frame_layout())
|
||||
card_colors.set_label('UV 颜色:')
|
||||
with Row(options_frame.get_frame_layout(), margins=style.scale([5, 0, 5, 5])) as color_row2:
|
||||
selected_card_frame_color = ColorPicker('UVEditorUVFrameSelectedColorPicker', color_row2.layout())
|
||||
selected_card_frame_color.set_rgb_colors(clr_to_float(literal_eval(self.options_manager.get('UVEditorUVFrameSelectedColor'))))
|
||||
selected_card_frame_color.connect_command(self.update_colors)
|
||||
deselected_card_frame_color = ColorPicker('UVEditorUVFrameDeselectedColorPicker', color_row2.layout())
|
||||
deselected_card_frame_color.set_rgb_colors(clr_to_float(literal_eval(self.options_manager.get('UVEditorUVFrameDeselectedColor'))))
|
||||
deselected_card_frame_color.connect_command(self.update_colors)
|
||||
card_fill_color = ColorPicker('UVEditorUVCardFillColorPicker', color_row2.layout())
|
||||
card_fill_color.set_rgb_colors(clr_to_float(literal_eval(self.options_manager.get('UVEditorUVCardFillColor'))))
|
||||
card_fill_color.connect_command(self.update_colors)
|
||||
main_column_layout.addWidget(separator())
|
||||
main_row_layout = main_row.layout()
|
||||
assert main_row_layout is not None
|
||||
main_row_layout.addWidget(self.editor)
|
||||
if MAYA_VER in [2020, 2022]:
|
||||
if not self.options_manager.get('UVBugMessageDismissed'):
|
||||
with Row(main_layout) as row:
|
||||
label = Label(row.layout())
|
||||
label.set_label('<p style="color:#FF542F">如何修复 Maya 2020 - 2022 的 UV 问题:</p>')
|
||||
link_button = Button(row.layout())
|
||||
link_button.set_label('打开修复方法')
|
||||
link_button.set_button_style('small-filled')
|
||||
link_button.clicked.connect(lambda: open_link('https://gs-curvetools.readthedocs.io/en/latest/faq.html#maya-2020-2022-and-broken-uvs'))
|
||||
dismiss_message = Button(row.layout())
|
||||
dismiss_message.set_label('忽略提示')
|
||||
dismiss_message.set_button_style('small-filled')
|
||||
dismiss_message.clicked.connect(lambda: row.setHidden(True))
|
||||
dismiss_message.clicked.connect(lambda: self.options_manager.set('UVBugMessageDismissed', 1))
|
||||
|
||||
def update_colors(self, *_):
|
||||
"""更新 UV 编辑器的颜色"""
|
||||
if self.editor:
|
||||
self.options_manager.save()
|
||||
try:
|
||||
self.editor.change_color(literal_eval(self.options_manager.get('UVEditorBGColor')), literal_eval(self.options_manager.get('UVEditorGridColor')), literal_eval(self.options_manager.get('UVEditorFrameColor')), literal_eval(self.options_manager.get('UVEditorUVCardFillColor')), literal_eval(self.options_manager.get('UVEditorUVFrameSelectedColor')))
|
||||
except BaseException as exc:
|
||||
self.editor.change_color()
|
||||
logger.exception(exc)
|
||||
self.editor.update()
|
||||
|
||||
@no_undo
|
||||
def update_editor(self):
|
||||
"""更新 UV 编辑器"""
|
||||
self.update_item_list()
|
||||
self.update_uvs()
|
||||
self.update_texture()
|
||||
|
||||
def show_all(self):
|
||||
"""显示所有 UV"""
|
||||
self.uv_list.expandAll()
|
||||
self.uv_list.selectAll()
|
||||
self.update_visibility()
|
||||
self.isolate_mode = False
|
||||
|
||||
@no_undo
|
||||
def update_uvs(self, keep_selection=False):
|
||||
"""更新 UV"""
|
||||
sel = mc.filterExpand(mc.ls(sl=1, fl=1, l=1), sm=9)
|
||||
old_uvs = self.editor.uv_dict
|
||||
self.editor.purge_uvs()
|
||||
if not sel:
|
||||
return
|
||||
sel = self.check_for_legacy_uvs(sel)
|
||||
assert sel is not None
|
||||
for item in sel:
|
||||
if mc.attributeQuery('Orientation', n=item, ex=1) and mc.connectionInfo(item + '.Orientation', isSource=1):
|
||||
if mc.attributeQuery('gsmessage', n=item, ex=1) and mc.listConnections(item + '.gsmessage'):
|
||||
curves = self.core.utils.get_all_connected_curves(item)
|
||||
else:
|
||||
curves = [item]
|
||||
for curve in curves:
|
||||
attrs = self.core.attributes.get_uvs(curve)
|
||||
checkbox = self.core.attributes.get_checkboxes(curve)
|
||||
if attrs:
|
||||
uv_item = self.editor.create_uv(curve)
|
||||
if 'flipUV' in checkbox:
|
||||
uv_item.flip = not checkbox['flipUV']
|
||||
uv_item.move_uv(x=attrs['moveU'], y=attrs['moveV'], rot=attrs['rotateUV'] * -1, s_x=attrs['scaleU'], s_y=attrs['scaleV'])
|
||||
if keep_selection:
|
||||
new_uvs = self.editor.uv_dict
|
||||
for key, val in new_uvs.items():
|
||||
if key in old_uvs:
|
||||
val.setSelected(True)
|
||||
|
||||
def check_for_legacy_uvs(self, curves):
|
||||
"""检查是否存在旧版 UV"""
|
||||
legacy_curves = []
|
||||
for curve in curves:
|
||||
root = mc.attributeQuery('rotateRootUV', n=curve, ex=1)
|
||||
tip = mc.attributeQuery('rotateTipUV', n=curve, ex=1)
|
||||
if root or tip:
|
||||
root_value = mc.getAttr(curve + '.rotateRootUV')
|
||||
tip_value = mc.getAttr(curve + '.rotateTipUV')
|
||||
if root_value or tip_value:
|
||||
legacy_curves.append(curve)
|
||||
if not legacy_curves:
|
||||
return curves
|
||||
logger.warning('检测到非零旧版 UV 属性', in_view=True)
|
||||
dialog = mc.confirmDialog(title='旧版 UV', message='检测到非零旧版 UV。\nUV 编辑器与此不兼容。\n是否将其归零以继续?', icon='warning', button=['是', '取消'], cancelButton='取消', dismissString='取消')
|
||||
if dialog == '是':
|
||||
for curve in legacy_curves:
|
||||
mc.setAttr(curve + '.rotateRootUV', 0)
|
||||
mc.setAttr(curve + '.rotateTipUV', 0)
|
||||
return curves
|
||||
elif dialog == '取消':
|
||||
dialog = mc.confirmDialog(title='旧版 UV', message='UV 编辑器与旧版 UV 不兼容。\n部分卡片将被忽略。', icon='信息', button='确定', cancelButton='确定', dismissString='确定')
|
||||
return list(set(curves) - set(legacy_curves))
|
||||
|
||||
@no_undo
|
||||
def update_texture(self):
|
||||
"""更新纹理"""
|
||||
sel = self.core.utils.select_part(2, just_return=True)
|
||||
if not sel:
|
||||
self.editor.remove_texture()
|
||||
self.editor.diffuse_path = ''
|
||||
return
|
||||
geo = mc.filterExpand(sel, sm=12)
|
||||
if not geo:
|
||||
return
|
||||
dag = mc.ls(geo[-1], dag=1, s=1)
|
||||
shader = mc.listConnections(dag, d=1, s=1, t='shadingEngine')
|
||||
if not shader:
|
||||
return
|
||||
diffuse_file_node, alpha_file_node = self.core.utils.find_diffuse_alpha(shader[0])
|
||||
if not diffuse_file_node and (not alpha_file_node):
|
||||
return
|
||||
texture_path = None
|
||||
alpha_path = None
|
||||
if diffuse_file_node:
|
||||
texture_path = mc.getAttr(diffuse_file_node + '.fileTextureName')
|
||||
if alpha_file_node:
|
||||
alpha_path = mc.getAttr(alpha_file_node + '.fileTextureName') if alpha_file_node else None
|
||||
if not texture_path and alpha_path:
|
||||
texture_path = alpha_path
|
||||
diffuse_file_node = alpha_file_node
|
||||
place_2d_texture = None
|
||||
if mc.attributeQuery('coverage', n=diffuse_file_node, ex=1):
|
||||
info = mc.connectionInfo(diffuse_file_node + '.coverage', sfd=1)
|
||||
if info:
|
||||
place_2d_texture = info.split('.')
|
||||
if place_2d_texture:
|
||||
place_2d_texture = place_2d_texture[0]
|
||||
cov, trans = (None, None)
|
||||
if place_2d_texture and mc.objExists(place_2d_texture) and self.widget_manager.get('UVEditorUseTransforms').isChecked() and mc.attributeQuery('coverage', ex=1, n=place_2d_texture) and mc.attributeQuery('translateFrame', ex=1, n=place_2d_texture):
|
||||
try:
|
||||
cov = mc.getAttr(place_2d_texture + '.coverage')[0]
|
||||
trans = mc.getAttr(place_2d_texture + '.translateFrame')[0]
|
||||
except BaseException as exc:
|
||||
logger.exception(exc)
|
||||
coverage = cov if cov else (1.0, 1.0)
|
||||
translation = (trans[0], trans[1]) if trans and trans else (0, 0)
|
||||
if not self.widget_manager.get('UVEditorTransparencyToggle').isChecked() and (not self.widget_manager.get('UVEditorAlphaOnlyToggle').isChecked()):
|
||||
alpha_path = None
|
||||
if texture_path:
|
||||
_, ext = os.path.splitext(texture_path)
|
||||
try:
|
||||
supported_formats = [str(x, encoding='ASCII').lower() for x in QtGui.QImageReader.supportedImageFormats()]
|
||||
except TypeError:
|
||||
supported_formats = [str(x).decode('ASCII').lower() for x in QtGui.QImageReader.supportedImageFormats()]
|
||||
if ext and ext[1:].lower() not in supported_formats:
|
||||
logger.warning('{} 格式不受支持。请使用 JPG/JPEG、PNG、TIF/TIFF(LZW 或无压缩)、TGA(24 位,无 RLE)'.format(ext[1:].upper()))
|
||||
return
|
||||
err = self.editor.set_texture('%s' % texture_path, alpha_path, coverage, translation)
|
||||
if err == 'SamePath':
|
||||
return
|
||||
if err == 'NoTexture':
|
||||
logger.warning('无法加载纹理文件。')
|
||||
elif err == 'ZeroTexture':
|
||||
logger.warning('无效路径或零纹理。请检查导出的纹理位深度和压缩方式。TIF/TIFF(LZW 或无压缩)、TGA(24 位,无 RLE)。')
|
||||
|
||||
def manual_curve_update(self):
|
||||
"""手动更新 UV 曲线"""
|
||||
try:
|
||||
self._update_curves()
|
||||
except BaseException as exc:
|
||||
logger.exception(exc)
|
||||
finally:
|
||||
self._stop_curves_update()
|
||||
|
||||
def _update_curves(self):
|
||||
if self.uv_update_check == 0:
|
||||
mc.undoInfo(ock=1, cn='gsUVUpdate')
|
||||
self.uv_update_check = 1
|
||||
sel = mc.filterExpand(mc.ls(sl=1), sm=9)
|
||||
uvs = self.editor.get_uvs()
|
||||
self.current_selection *= 0
|
||||
if not sel:
|
||||
return
|
||||
for curve in sel:
|
||||
if curve in uvs:
|
||||
self.current_selection.append(curve)
|
||||
elif mc.attributeQuery('gsmessage', n=curve, ex=1) and mc.listConnections(curve + '.gsmessage'):
|
||||
bound_curves = self.core.utils.get_all_connected_curves(curve)
|
||||
if bound_curves:
|
||||
self.current_selection += bound_curves
|
||||
if not self.timer.increment(0.016666666666666666):
|
||||
return
|
||||
if not self.current_selection:
|
||||
return
|
||||
uvs = self.editor.get_uvs()
|
||||
for curve in self.current_selection:
|
||||
if curve in uvs:
|
||||
self.core.attributes.set_attributes(curve, uvs[curve])
|
||||
|
||||
def _stop_curves_update(self):
|
||||
if self.uv_update_check == 1:
|
||||
mc.undoInfo(cck=1)
|
||||
self.uv_update_check = 0
|
||||
self.current_selection *= 0
|
||||
self.core.curve_control.update_ui()
|
||||
|
||||
def update_buttons(self, controller_mode, scale_mode):
|
||||
"""更新按钮状态"""
|
||||
assert self.controller_group is not None
|
||||
assert self.direction_switch is not None
|
||||
buttons = self.controller_group.buttons()
|
||||
direction = self.direction_switch.buttons()
|
||||
if controller_mode == 'SELECT':
|
||||
buttons[0].setChecked(True)
|
||||
elif controller_mode == 'MOVE':
|
||||
buttons[1].setChecked(True)
|
||||
elif controller_mode == 'ROTATE':
|
||||
buttons[2].setChecked(True)
|
||||
elif controller_mode == 'SCALE':
|
||||
buttons[3].setChecked(True)
|
||||
if scale_mode == 'H':
|
||||
direction[0].setChecked(True)
|
||||
else:
|
||||
direction[1].setChecked(True)
|
||||
elif controller_mode == 'DRAW':
|
||||
buttons[4].setChecked(True)
|
||||
|
||||
def update_controller_mode(self):
|
||||
"""更新控制模式"""
|
||||
assert self.controller_group is not None
|
||||
assert self.direction_switch is not None
|
||||
button_id = self.controller_group.checkedId()
|
||||
scale_id = self.direction_switch.checkedId()
|
||||
scale = 'H'
|
||||
if button_id == 0:
|
||||
mode = 'SELECT'
|
||||
elif button_id == 1:
|
||||
mode = 'MOVE'
|
||||
elif button_id == 2:
|
||||
mode = 'ROTATE'
|
||||
elif button_id == 3:
|
||||
mode = 'SCALE'
|
||||
if scale_id == 0:
|
||||
scale = 'H'
|
||||
else:
|
||||
scale = 'V'
|
||||
else:
|
||||
mode = 'DRAW'
|
||||
self.editor.controller_mode_change(mode, scale)
|
||||
|
||||
def update_item_list(self):
|
||||
"""更新项目列表"""
|
||||
sel = mc.filterExpand(mc.ls(sl=1), sm=9)
|
||||
if not sel:
|
||||
self.uv_list.clear_item_list()
|
||||
return
|
||||
final_dict = {}
|
||||
for curve in sel:
|
||||
if mc.attributeQuery('Orientation', n=curve, ex=1) and mc.connectionInfo(curve + '.Orientation', isSource=1):
|
||||
if mc.attributeQuery('gsmessage', n=curve, ex=1) and mc.listConnections(curve + '.gsmessage'):
|
||||
bound_curves = self.core.utils.get_all_connected_curves(curve)
|
||||
final_dict[curve] = bound_curves
|
||||
else:
|
||||
final_dict[curve] = []
|
||||
self.uv_list.update_item_list(final_dict)
|
||||
|
||||
def update_visibility(self):
|
||||
"""更新 UV 的可见性"""
|
||||
item_list = self.uv_list.get_selection()
|
||||
items = self.editor.get_all_uvs()
|
||||
for item in items:
|
||||
if item.name in item_list:
|
||||
item.setVisible(True)
|
||||
else:
|
||||
item.setVisible(False)
|
||||
self.editor.update()
|
||||
|
||||
def hide(self):
|
||||
"""隐藏选中的 UV 项目"""
|
||||
sel_uvs = self.editor.get_all_uvs(selected=True)
|
||||
sel_uvs_list = [i.name for i in sel_uvs]
|
||||
for uv_item in sel_uvs:
|
||||
uv_item.setVisible(False)
|
||||
item_list = self.uv_list.get_item_list()
|
||||
select_list = []
|
||||
for item in item_list:
|
||||
if item.curve_name in sel_uvs_list:
|
||||
select_list.append(item)
|
||||
self.uv_list.select_items(select_list)
|
||||
|
||||
def isolate_select(self):
|
||||
"""隔离选中的 UV 项目"""
|
||||
all_uvs = self.editor.get_all_uvs()
|
||||
sel_uvs = self.editor.get_all_uvs(selected=True)
|
||||
if not sel_uvs:
|
||||
return
|
||||
if self.isolate_mode:
|
||||
self.show_all()
|
||||
self.isolate_mode = False
|
||||
return
|
||||
self.isolate_mode = True
|
||||
sel_uvs_list = [i.name for i in sel_uvs]
|
||||
for uv_item in all_uvs:
|
||||
if uv_item.name not in sel_uvs_list:
|
||||
uv_item.setVisible(False)
|
||||
item_list = self.uv_list.get_item_list()
|
||||
deselect_list = []
|
||||
for item in item_list:
|
||||
if item.curve_name not in sel_uvs_list:
|
||||
deselect_list.append(item)
|
||||
self.uv_list.select_items(deselect_list)
|
||||
|
||||
def horizontal_flip_uv(self):
|
||||
"""水平翻转 UV"""
|
||||
items = self.editor.get_all_uvs(selected=True)
|
||||
for item in items:
|
||||
if item.name and mc.attributeQuery('flipUV', n=item.name, ex=1):
|
||||
flip = mc.getAttr(item.name + '.flipUV')
|
||||
mc.setAttr(item.name + '.flipUV', not flip)
|
||||
item.flip = flip
|
||||
item.update()
|
||||
if items:
|
||||
self.editor.update()
|
||||
|
||||
def vertical_flip_uv(self):
|
||||
"""垂直翻转 UV"""
|
||||
self.editor.vertical_flip_uvs()
|
||||
self.manual_curve_update()
|
||||
|
||||
def function_switch(self, key):
|
||||
"""功能切换"""
|
||||
if key == 'H':
|
||||
self.horizontal_flip_uv()
|
||||
elif key == 'V':
|
||||
self.vertical_flip_uv()
|
||||
elif key == 'X':
|
||||
self.reset_uvs()
|
||||
elif key == 'I':
|
||||
self.isolate_select()
|
||||
elif key == 'O':
|
||||
self.hide()
|
||||
elif key == 'A':
|
||||
self.show_all()
|
||||
elif key == 'S':
|
||||
self.sync_selection()
|
||||
|
||||
def reset_uvs(self):
|
||||
"""重置 UV"""
|
||||
self.editor.reset_uvs()
|
||||
self.manual_curve_update()
|
||||
|
||||
def randomize_uvs(self):
|
||||
"""随机化 UV"""
|
||||
if get_mod() == 'Shift':
|
||||
self.editor.randomize_uvs(True)
|
||||
else:
|
||||
self.editor.randomize_uvs(False)
|
||||
self.manual_curve_update()
|
||||
|
||||
def sync_selection(self):
|
||||
"""根据 UV 编辑器的选择在 Maya 视口中选择曲线"""
|
||||
sel = mc.filterExpand(mc.ls(sl=1), sm=9)
|
||||
if not sel:
|
||||
return
|
||||
sel_uvs = [x.name for x in self.editor.get_all_uvs(selected=True)]
|
||||
new_sel = [x for x in sel if x in sel_uvs]
|
||||
if new_sel:
|
||||
mc.select(new_sel, r=1)
|
||||
|
||||
@deferred_lp
|
||||
def x():
|
||||
uvs = self.editor.get_all_uvs()
|
||||
for uv_item in uvs:
|
||||
uv_item.setSelected(True)
|
||||
self.editor.scene().update()
|
||||
x()
|
||||
Reference in New Issue
Block a user