# 字节码版本: 3.11a7e (3495)
# 源代码时间戳: 2025-04-22 11:47:05 UTC (1745322425)
"""
GS曲线工具主窗口
---
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
"""
import os
from functools import partial
import maya.cmds as mc
from gs_curvetools.api.maya_tools import deferred, get_mod, no_undo, undo
from gs_curvetools.api.qt_compat import *
from gs_curvetools.api.typing 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.debug.logger import logger
from gs_curvetools.managers.options_manager import OptionsManager
from gs_curvetools.managers.script_jobs import ScriptJobs
from gs_curvetools.ui import style
from gs_curvetools.ui.components import AboutWindow, AttributesFilterWindow, CurveControlWindow, CurveThicknessWindow, CustomLayerColorsWindow, GeoToCurveWindow, RandomizeCurveWindow, ScaleFactorWindow, UVEditorWindow
from gs_curvetools.ui.tooltips import Tooltips
from gs_curvetools.ui.utils import maya_dockable_window
from gs_curvetools.ui.widgets import ActionGroup, Button, Frame, Layer, LayerCollectionWidget, Layout, LineEdit, Menu, MenuItem, Row, get_unique_name, maya_slider, separator, wrap_control
if TYPE_CHECKING:
from gs_curvetools.core.core import Core
from gs_curvetools.managers.widget_manager import WidgetManager
class MainWindow(QWidget):
"""Main GS CurveTools window"""
def __init__(self, manager, core):
menu_height = 786 if MAYA_VER <= 2024 else 810
dockable_window = maya_dockable_window(name=WINDOWS.MainWindow.name, label=WINDOWS.MainWindow.label, i_h=menu_height)
mc.workspaceControl(WINDOWS.MainWindow.name, e=1, ui=UI_SCRIPT)
super(MainWindow, self).__init__(dockable_window)
self.setParent(dockable_window)
dockable_layout = dockable_window.layout()
assert dockable_layout is not None
dockable_layout.addWidget(self)
self.widget_manager = manager
self.core = core
self.script_jobs = ScriptJobs.singleton()
self.script_jobs.check_script_jobs(WINDOWS.MainWindow.name)
self.options_manager = OptionsManager()
self.tooltips = Tooltips()
QtGui.QFontDatabase.removeAllApplicationFonts()
fonts = os.listdir(GetFolder.fonts())
for font in fonts:
QtGui.QFontDatabase.addApplicationFont(os.path.join(GetFolder.fonts(), font))
self.about_window = AboutWindow()
self.attributes_filter_window = AttributesFilterWindow(self)
self.curve_control_window = CurveControlWindow(self)
self.curve_thickness_window = CurveThicknessWindow(self)
self.custom_layer_colors_window = CustomLayerColorsWindow(self)
self.geo_to_curve_window = GeoToCurveWindow(self)
self.randomize_curve_window = RandomizeCurveWindow(self)
self.scale_factor_window = ScaleFactorWindow(self)
self.uv_editor = UVEditorWindow(self)
self.widget_manager.add(WINDOWS.MainWindow.name, dockable_window)
self._connect_signals()
def _connect_signals(self):
"""Sets up the signals"""
self.curve_control_window.update_curve_control_ui.connect(self.core.curve_control.update_ui)
self.curve_control_window.update_main_ui.connect(self.core.updates.update_main_ui)
def create_ui(self):
"""Creates the UI"""
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(*style.scale([2, 0, 2, 0]))
scroll_area = QScrollArea(self)
scroll_widget = QWidget()
scroll_widget.setFocus()
scroll_layout = QVBoxLayout(scroll_widget)
scroll_area.setWidget(scroll_widget)
main_layout.addWidget(scroll_area)
scroll_layout.setContentsMargins(0, 0, 0, 0)
scroll_layout.setSpacing(style.scale(2))
scroll_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
scroll_area.setFrameShape(QtWidgets.QFrame.Shape.NoFrame)
scroll_area.setWidgetResizable(True)
menu_bar_widget = QWidget(scroll_area)
menu_bar_layout = QHBoxLayout(menu_bar_widget)
menu_bar_layout.setContentsMargins(0, 0, 0, 0)
menu_bar_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
menu_bar = QMenuBar(menu_bar_widget)
menu_bar.setSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
menu_bar_layout.addWidget(menu_bar)
scroll_layout.addWidget(menu_bar_widget)
with Menu('选项', menu_bar) as menu:
menu.triggered.connect(self.options_manager.save)
menu.addSection('导入 / 导出')
import_curves = MenuItem('importCurves', '导入曲线', menu)
import_curves.triggered.connect(no_undo(self.core.import_export.import_curves))
export_curves = MenuItem('exportCurves', '导出曲线', menu)
export_curves.triggered.connect(self.core.import_export.export_curves)
menu.addSection('全局修改器')
change_scale_factor = MenuItem('changeScaleFactor', '更改比例因子和精度', menu)
change_scale_factor.triggered.connect(self.scale_factor_window.show_window)
global_curve_thickness = MenuItem('globalCurveThickness', '全局曲线厚度', menu)
global_curve_thickness.triggered.connect(self.curve_thickness_window.show_window)
menu.addSection('视图端口命令')
ao_settings = MenuItem('setAOSettings', '设置AO设置', menu)
ao_settings.triggered.connect(self.options_manager.set_ao_settings)
with Menu('透明度设置', menu) as transparency_settings_menu:
simple_transparency = MenuItem('setSimpleTransparency', '简单透明度(快速,不准确)', transparency_settings_menu)
simple_transparency.triggered.connect(partial(no_undo(self.options_manager.set_transparency_settings), 0))
object_sorting_transparency = MenuItem('setObjectSortingTransparency', '对象排序透明度(中等)', transparency_settings_menu)
object_sorting_transparency.triggered.connect(partial(no_undo(self.options_manager.set_transparency_settings), 1))
set_depth_transparency = MenuItem('setDepthTransparency', '深度透明度(准确,缓慢,推荐)', transparency_settings_menu)
set_depth_transparency.triggered.connect(partial(no_undo(self.options_manager.set_transparency_settings), 2))
menu.addSection('转换曲线')
with Menu('转换曲线', menu) as convert_curves_submenu:
convert_to_warp_card = MenuItem('convertToWarpCard', '转换为变形面片', convert_curves_submenu)
convert_to_warp_card.triggered.connect(partial(undo(self.core.functions.convert_selection_to), 0))
convert_to_warp_tube = MenuItem('convertToWarpTube', '转换为变形管', convert_curves_submenu)
convert_to_warp_tube.triggered.connect(partial(undo(self.core.functions.convert_selection_to), 1))
convert_to_extrude_card = MenuItem('convertToExtrudeCard', '转换为挤压面片', convert_curves_submenu)
convert_to_extrude_card.triggered.connect(partial(undo(self.core.functions.convert_selection_to), 2))
convert_to_extrude_tube = MenuItem('convertToExtrudeTube', '转换为挤压管', convert_curves_submenu)
convert_to_extrude_tube.triggered.connect(partial(undo(self.core.functions.convert_selection_to), 3))
menu.addSection('实用功能')
duplicate_unparent = MenuItem('duplicateUnparentCurves', '复制并取消父级关联曲线', menu)
duplicate_unparent.triggered.connect(undo(self.core.functions.duplicate_unparent))
menu.addSection('常规选项')
MenuItem('keepCurveAttributes', '保留曲线属性', menu, True, self.options_manager.get('keepCurveAttributes'))
MenuItem('addBetweenBlendAttributes', '添加面片/管混合属性', menu, True, self.options_manager.get('addBetweenBlendAttributes'))
MenuItem('fillCreateCurvesOnly', '填充仅创建曲线', menu, True, self.options_manager.get('fillCreateCurvesOnly'))
MenuItem('convertInstances', '自动转换实例', menu, True, self.options_manager.get('convertInstances'))
MenuItem('useAutoSamplingOnNewCurves', '对新曲线使用自动采样', menu, True, self.options_manager.get('useAutoSamplingOnNewCurves'))
MenuItem('useAutoRefineOnNewCurves', '对新曲线使用自动细化', menu, True, self.options_manager.get('useAutoRefineOnNewCurves'))
MenuItem('flipUVsAfterMirror', '镜像后翻转UV', menu, True, self.options_manager.get('flipUVsAfterMirror'))
enable_tooltips_menu = MenuItem('enableTooltips', '启用工具提示', menu, True, self.options_manager.get('enableTooltips'))
enable_tooltips_menu.triggered.connect(self.toggle_tooltips)
menu.addSection('颜色选项')
sync_color = MenuItem('syncCurveColor', '同步曲线颜色到图层颜色', menu, True, self.options_manager.get('syncCurveColor'))
sync_color.triggered.connect(self.core.color_mode.sync_curve_colors)
MenuItem('colorizedRegroup', '对重新分组的图层进行颜色化', menu, True, self.options_manager.get('colorizedRegroup'))
color_only_diffuse = MenuItem('colorOnlyDiffuse', '颜色仅影响漫反射', menu, True, self.options_manager.get('colorOnlyDiffuse'))
color_only_diffuse.triggered.connect(self.core.color_mode.update_color_options)
checker_pattern = MenuItem('checkerPattern', '颜色模式使用棋盘格图案', menu, True, self.options_manager.get('checkerPattern'))
checker_pattern.triggered.connect(self.core.color_mode.update_color_options)
menu.addSection('绑定选项')
MenuItem('boundCurvesFollowParent', '绑定曲线跟随父级', menu, True, self.options_manager.get('boundCurvesFollowParent'))
MenuItem('massBindOption', '绑定到所有可用的空曲线', menu, True, self.options_manager.get('massBindOption'))
MenuItem('bindDuplicatesCurves', '绑定前复制曲线', menu, True, self.options_manager.get('bindDuplicatesCurves'))
MenuItem('bindFlipUVs', '绑定前翻转UV', menu, True, self.options_manager.get('bindFlipUVs'))
menu.addSection('解绑/解包选项')
MenuItem('unpackDeleteOriginalObject', '解包时删除原始对象', menu, True, self.options_manager.get('unpackDeleteOriginalObject'))
MenuItem('unpackCVMatch', '解包匹配原始CV数量', menu, True, self.options_manager.get('unpackCVMatch'))
menu.addSection('图层选项')
MenuItem('ignoreLastLayer', '忽略最后一个图层', menu, True, self.options_manager.get('ignoreLastLayer'))
MenuItem('syncOutlinerLayerVis', '同步大纲/图层可见性', menu, True, self.options_manager.get('syncOutlinerLayerVis'))
MenuItem('replacingCurveLayerSelection', '替换曲线图层选择', menu, True, self.options_manager.get('replacingCurveLayerSelection'))
only_numbers_in_layers = MenuItem('layerNumbersOnly', '图层仅使用数字', menu, True, self.options_manager.get('layerNumbersOnly'))
only_numbers_in_layers.triggered.connect(self.core.layer_manager.change_layers_to_numbers)
only_numbers_in_layers.triggered.connect(self.update_layer_list)
with Menu('活动图层数量', menu) as layer_number_menu, ActionGroup('layerRowsActionGroup', layer_number_menu) as action_group:
MenuItem('2layerRows', '20个图层', layer_number_menu, True, self.options_manager.get('2layerRows'), collection=action_group)
MenuItem('3layerRows', '30个图层', layer_number_menu, True, self.options_manager.get('3layerRows'), collection=action_group)
MenuItem('4layerRows', '40个图层', layer_number_menu, True, self.options_manager.get('4layerRows'), collection=action_group)
MenuItem('6layerRows', '60个图层', layer_number_menu, True, self.options_manager.get('6layerRows'), collection=action_group)
MenuItem('8layerRows', '80个图层', layer_number_menu, True, self.options_manager.get('8layerRows'), collection=action_group)
action_group.triggered.connect(self.core.updates.update_main_ui)
menu.addSection('图层集合选项')
MenuItem('ignoreTemplateCollections', '忽略“模板”集合名称', menu, True, self.options_manager.get('ignoreTemplateCollections'))
MenuItem('groupTemplateCollections', '将“模板”集合分组在一起', menu, True, self.options_manager.get('groupTemplateCollections'))
layer_collections_toggle = MenuItem('showLayerCollectionsMenu', '显示图层集合菜单', menu, True, self.options_manager.get('showLayerCollectionsMenu'))
layer_collections_toggle.triggered.connect(self.core.layer_manager.layer_collections.toggle_widget)
MenuItem('importIntoANewCollection', '导入到新集合中', menu, True, self.options_manager.get('importIntoANewCollection'))
menu.addSection('其他选项')
with Menu('其他选项', menu) as other_options_menu:
other_options_menu.addSection('更新功能')
convert_to_new_layer_system = MenuItem('convertToNewLayerSystem', '转换为新图层系统', other_options_menu)
convert_to_new_layer_system.triggered.connect(undo(self.core.utils.convert_to_new_layer_system))
update_layers = MenuItem('updateLayers', '更新图层', other_options_menu)
update_layers.triggered.connect(undo(self.core.layer_manager.delete_unused_layers))
from gs_curvetools.main import reset as reset_gs_curvetools
reset_to_defaults = MenuItem('resetToDefaults', '恢复默认设置', other_options_menu)
reset_to_defaults.triggered.connect(reset_gs_curvetools)
other_options_menu.addSection('修复')
maya_2020_uv_fix = MenuItem('maya2020UVFix', '修复Maya 2020 - 2022 UV错误', other_options_menu)
maya_2020_uv_fix.triggered.connect(undo(self.core.fixes.fix_maya_2020_uvs))
fix_broken_graphs = MenuItem('mayaFixBrokenGraphs', '修复损坏的图表', other_options_menu)
fix_broken_graphs.triggered.connect(undo(self.core.fixes.fix_broken_graphs))
convert_bezier_to_nurbs = MenuItem('convertBezierToNurbs', '将所选贝塞尔曲线转换为NURBS曲线', other_options_menu)
convert_bezier_to_nurbs.triggered.connect(undo(self.core.fixes.convert_bezier_to_nurbs))
maya_2020_twist_fix = MenuItem('maya2020TwistAttribute', '修复Maya 2020.4扭曲属性', other_options_menu)
maya_2020_twist_fix.triggered.connect(undo(self.core.fixes.fix_maya_2020_twist))
maya_2020_unbind_fix = MenuItem('maya2020UnbindFix', '修复Maya 2020.4解绑功能', other_options_menu)
maya_2020_unbind_fix.triggered.connect(undo(self.core.fixes.fix_maya_2020_unbind))
delete_all_animation_keys = MenuItem('deleteAllAnimationKeys', '删除所有动画关键帧', other_options_menu)
delete_all_animation_keys.triggered.connect(undo(self.core.fixes.delete_keys_on_all_objects))
disable_curve_smooth = MenuItem('disableCurveSmooth', '重置旧版曲线平滑属性', other_options_menu)
disable_curve_smooth.triggered.connect(self.core.fixes.reset_curve_smooth)
reset_legacy_magnitude = MenuItem('resetLegacyMagnitude', '重置旧版轮廓幅度属性', other_options_menu)
reset_legacy_magnitude.triggered.connect(self.core.fixes.reset_legacy_profile_magnitude)
with Menu('帮助', menu_bar) as menu:
open_log_file = MenuItem('openLogFile', '打开日志文件', menu)
open_log_file.triggered.connect(logger.open_log_file)
open_online_documentation = MenuItem('openOnlineDocumentation', '打开在线文档', menu)
open_online_documentation.triggered.connect(lambda: open_link(USER_DOCS))
useful_links = MenuItem('usefulLinks', '有用的链接和联系方式', menu)
useful_links.triggered.connect(self.about_window.social_window)
with Menu('关于', menu_bar) as menu:
about_action = MenuItem('gsAbout', '关于', menu)
about_action.triggered.connect(self.about_window.about_window)
menu.addSeparator()
menu.addAction('Made by George Sladkovsky (%s)' % YEAR).setEnabled(False)
scroll_layout.addWidget(separator())
with Row(scroll_layout) as row:
extrude_warp_switch_group = QtWidgets.QButtonGroup(scroll_layout.widget())
self.widget_manager.add('gsExtrudeWarpSwitchGroup', extrude_warp_switch_group)
extrude_warp_switch_group.buttonToggled.connect(self.extrude_warp_toggle)
extrude_warp_switch_group.buttonClicked.connect(self.options_manager.save)
warp_switch = Button(row.layout(), 'warpSwitch')
warp_switch.set_label('变形', line_height=100)
warp_switch.set_button_style('small')
warp_switch.setCheckable(True)
extrude_switch = Button(row.layout(), 'extrudeSwitch')
extrude_switch.set_label('挤压', line_height=100)
extrude_switch.set_button_style('small')
extrude_switch.setCheckable(True)
extrude_warp_switch_group.addButton(warp_switch, 0)
extrude_warp_switch_group.addButton(extrude_switch, 1)
with Row(scroll_layout) as row:
new_card = Button(row.layout(), 'newCard')
new_card.set_label('新面片')
new_card.clicked.connect(partial(undo(self.core.create.new), 0))
new_tube = Button(row.layout(), 'newTube')
new_tube.set_label('新管')
new_tube.clicked.connect(partial(undo(self.core.create.new), 1))
with Row(scroll_layout) as row:
curve_card = Button(row.layout(), 'curveCard')
curve_card.set_label('曲线面片')
curve_card.clicked.connect(partial(undo(self.core.create.from_curves), 0))
curve_tube = Button(row.layout(), 'curveTube')
curve_tube.set_label('曲线管')
curve_tube.clicked.connect(partial(undo(self.core.create.from_curves), 1))
with Row(scroll_layout) as row:
bind = Button(row.layout(), 'gsBind')
bind.set_label('绑定')
bind.set_icon('mod-top')
bind.clicked.connect(undo(self.core.create.bind))
unbind = Button(row.layout(), 'gsUnbind')
unbind.set_label('解绑')
unbind.set_icon('mod-top')
unbind.clicked.connect(undo(lambda: self.core.create.unpack() if get_mod() == 'Shift' else self.core.create.unbind()))
scroll_layout.addWidget(separator())
with Row(scroll_layout) as row:
add_cards = Button(row.layout(), 'addCards')
add_cards.set_label('添加面片')
add_cards.set_icon('mod-top')
add_cards.clicked.connect(partial(undo(self.core.create.add_between), 0))
add_tubes = Button(row.layout(), 'addTubes')
add_tubes.set_label('添加管')
add_tubes.set_icon('mod-top')
add_tubes.clicked.connect(partial(undo(self.core.create.add_between), 1))
with Row(scroll_layout) as row:
fill = Button(row.layout(), 'gsFill')
fill.set_label('填充')
fill.set_icon('mod-top')
fill.clicked.connect(undo(self.core.create.fill))
subdivide = Button(row.layout(), 'gsSubdivide')
subdivide.set_label('细分')
subdivide.set_icon('mod-top')
subdivide.clicked.connect(undo(self.core.functions.subdivide_curve))
with Row(scroll_layout) as row:
m_add_cards_slider = mc.intSliderGrp('gsCurvesSlider', l='添加', f=1, adj=3, cw=[(1, 20), (2, 25), (3, 1)], min=1, max=10, v=3)
add_cards_slider = maya_slider(m_add_cards_slider, layout=row.layout())
self.widget_manager.add('gsCurvesSlider', add_cards_slider)
add_cards_slider.setContentsMargins(0, 0, 0, 0)
c = add_cards_slider.children()[2]
if isinstance(c, QWidget):
c.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
scroll_layout.addWidget(separator())
with Row(scroll_layout) as row:
edge_to_curve = Button(row.layout(), 'gsEdgeToCurve')
edge_to_curve.set_label('边转曲线')
edge_to_curve.set_icon('mod-top')
edge_to_curve.clicked.connect(undo(self.core.conversion.edge_to_curve))
geo_to_curve = Button(row.layout(), 'gsGeoToCurve')
geo_to_curve.set_label('几何体转曲线')
geo_to_curve.clicked.connect(self.geo_to_curve_window.open_ui)
scroll_layout.addWidget(separator())
with Row(scroll_layout, obj_name='LayerCollectionsLayout', spacing=1) as layer_collections_layout:
layer_combo_box = LayerCollectionWidget('layerCollectionsComboBox')
layer_combo_box.setStyleSheet(style.SMALL_COMBO_BOX)
def collection_changed():
self.core.updates.update_visibility_based_on_active_collection()
self.core.updates.update_main_ui()
layer_combo_box.currentIndexChanged.connect(lambda *_: deferred(collection_changed)())
layer_combo_box.setSizeAdjustPolicy(QtWidgets.QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon)
layer_combo_box.setDuplicatesEnabled(False)
layer_combo_box.setMinimumWidth(0)
layer_combo_box.setFixedHeight(style.scale(16))
mc.popupMenu(mm=1, p=layer_combo_box.objectName())
mc.menuItem(rp='N', l='清除', c=lambda *_: undo(self.core.layer_manager.layer_collections.clear)())
mc.menuItem(rp='E', l='复制', c=lambda *_: no_undo(self.core.layer_manager.layer_collections.copy)())
mc.menuItem(rp='NE', l='上移', c=lambda *_: no_undo(self.core.layer_manager.layer_collections.move_up)())
mc.menuItem(rp='NW', l='合并上移', c=lambda *_: no_undo(self.core.layer_manager.layer_collections.merge_up)())
mc.menuItem(rp='W', l='粘贴', c=lambda *_: undo(self.core.layer_manager.layer_collections.paste)())
mc.menuItem(rp='SW', l='合并下移', c=lambda *_: no_undo(self.core.layer_manager.layer_collections.merge_down)())
mc.menuItem(rp='SE', l='下移', c=lambda *_: no_undo(self.core.layer_manager.layer_collections.move_down)())
mc.menuItem(rp='S', l='重命名', c=lambda *_: no_undo(self.core.layer_manager.layer_collections.rename)())
with Row(layer_collections_layout.layout(), margins=style.scale([1, 0, 0, 0])) as plus_minus_buttons_layout:
layer_combo_plus = Button(obj_name='layerCollectionsPlus', layout=plus_minus_buttons_layout.layout())
layer_combo_plus.setFixedHeight(style.scale(16))
layer_combo_plus.set_label('+', line_height=100)
layer_combo_plus.setMinimumWidth(1)
layer_combo_plus.set_button_style('small-filled')
layer_combo_plus.clicked.connect(self.core.layer_manager.layer_collections.create)
layer_combo_minus = Button(obj_name='layerCollectionsMinus', layout=plus_minus_buttons_layout.layout())
layer_combo_minus.setEnabled(False)
layer_combo_minus.setFixedHeight(style.scale(16))
layer_combo_minus.setMinimumWidth(1)
layer_combo_minus.set_label('-', line_height=100)
layer_combo_minus.set_button_style('small-filled')
layer_combo_minus.clicked.connect(undo(self.core.layer_manager.layer_collections.delete))
lcl = layer_collections_layout.layout()
if isinstance(lcl, QHBoxLayout):
lcl.addWidget(layer_combo_box, 3)
lcl.addWidget(plus_minus_buttons_layout, 1)
layer_combo_box.insertItem(0, '主集合')
with Row(scroll_layout) as row:
all_filter = Button(row.layout(), 'gsAllFilter')
all_filter.set_button_style('small-filled')
all_filter.set_label('全部', line_height=100)
all_filter.set_icon('mod-top')
all_filter.clicked.connect(partial(undo(self.core.layer_manager.layers_filter_toggle), True, True))
curve_filter = Button(row.layout(), 'gsCurveFilter')
curve_filter.set_button_style('small-filled')
curve_filter.set_label('曲线', line_height=100)
curve_filter.clicked.connect(partial(undo(self.core.layer_manager.layers_filter_toggle), True, False, ignore=['Shift+Ctrl']))
mc.popupMenu(mm=1, p=curve_filter.objectName())
mc.menuItem(rp='N', l='切换始终置顶', c=lambda *_: undo(self.core.functions.always_on_top_toggle)())
mc.menuItem(rp='S', l='在非活动集合上自动隐藏曲线', cb=self.options_manager.get('AutoHideCurvesOnInactiveCollections'), c=undo(self.core.layer_manager.collection_visibility_toggle))
geo_filter = Button(row.layout(), 'gsGeoFilter')
geo_filter.set_button_style('small-filled')
geo_filter.set_label('几何体', line_height=100)
geo_filter.clicked.connect(partial(undo(self.core.layer_manager.layers_filter_toggle), False, True, ignore=['Shift+Ctrl']))
color_mode = Button(row.layout(), 'colorMode')
color_mode.set_button_style('small-filled')
color_mode.setCheckable(True)
color_mode.setChecked(False)
color_mode.set_label('颜色', line_height=100)
color_mode.clicked.connect(undo(self.core.color_mode.toggle_color_vis))
mc.popupMenu(mm=1, p=color_mode.objectName())
mc.menuItem(rp='N', l='随机化颜色', c=lambda *_: undo(self.core.color_mode.randomize_colors)())
mc.menuItem(rp='E', l='重置曲线颜色', c=lambda *_: self.core.color_mode.reset_curve_colors())
mc.menuItem(rp='W', l='应用曲线颜色', c=lambda *_: self.core.color_mode.sync_curve_colors(True))
mc.menuItem(rp='S', l='自定义颜色窗口', c=lambda *_: self.custom_layer_colors_window.window())
with Layout(scroll_layout, obj_name='LayerLayout') as layer_layout:
layer_button_grp = QtWidgets.QButtonGroup(layer_layout)
layer_button_grp.setObjectName(get_unique_name('LayerGroup'))
layer_button_grp.setExclusive(True)
self.widget_manager.add('LayerGroup', layer_button_grp)
with Row(layer_layout.layout(), 'layerRow0', spacing=0) as row:
for i in range(10):
layer_button_grp.addButton(self.selection_sets(i, row.layout(), str(i)))
with Row(layer_layout.layout(), 'layerRow1', spacing=0) as row:
letter = ord('A')
letters = [chr(i) for i in range(letter, letter + 10)]
if self.widget_manager.get('layerNumbersOnly').isChecked():
letters = list(range(10, 20))
for i in range(10):
layer_button_grp.addButton(self.selection_sets(i + 10, row.layout(), str(letters[i])))
with Row(layer_layout.layout(), 'layerRow2', spacing=0) as row:
for i in range(10):
layer_button_grp.addButton(self.selection_sets(i + 20, row.layout(), str(i + 20)))
self.widget_manager.get('layerRow2').setHidden(True)
with Row(layer_layout.layout(), 'layerRow3', spacing=0) as row:
for i in range(10):
layer_button_grp.addButton(self.selection_sets(i + 30, row.layout(), str(i + 30)))
self.widget_manager.get('layerRow3').setHidden(True)
with Row(layer_layout.layout(), 'layerRow4', spacing=0) as row:
for i in range(10):
layer_button_grp.addButton(self.selection_sets(i + 40, row.layout(), str(i + 40)))
self.widget_manager.get('layerRow4').setHidden(True)
with Row(layer_layout.layout(), 'layerRow5', spacing=0) as row:
for i in range(10):
layer_button_grp.addButton(self.selection_sets(i + 50, row.layout(), str(i + 50)))
self.widget_manager.get('layerRow5').setHidden(True)
with Row(layer_layout.layout(), 'layerRow6', spacing=0) as row:
for i in range(10):
layer_button_grp.addButton(self.selection_sets(i + 60, row.layout(), str(i + 60)))
self.widget_manager.get('layerRow6').setHidden(True)
with Row(layer_layout.layout(), 'layerRow7', spacing=0) as row:
for i in range(10):
layer_button_grp.addButton(self.selection_sets(i + 70, row.layout(), str(i + 70)))
self.widget_manager.get('layerRow7').setHidden(True)
self.widget_manager.get('curveGrp0').setChecked(True)
with Row(scroll_layout) as row:
extract_selected = Button(row.layout(), 'gsExtractSelected')
extract_selected.set_label('提取
所选')
extract_selected.set_icon('mod-bottom')
extract_selected.clicked.connect(undo(self.core.functions.extract_selected_curves))
extract_all = Button(row.layout(), 'gsExtractAll')
extract_all.set_label('提取
全部')
extract_all.set_icon('mod-bottom')
extract_all.clicked.connect(undo(self.core.functions.extract_all_curves))
scroll_layout.addWidget(separator())
with Row(scroll_layout) as row:
select_curve = Button(row.layout(), 'gsSelectCurve')
select_curve.set_label('选择
曲线')
select_curve.clicked.connect(partial(undo(self.core.utils.select_part), 1))
select_geo = Button(row.layout(), 'gsSelectGeo')
select_geo.set_label('选择
几何体')
select_geo.clicked.connect(partial(undo(self.core.utils.select_part), 2))
select_group = Button(row.layout(), 'gsSelectGroup')
select_group.set_label('选择
组')
select_group.clicked.connect(partial(undo(self.core.utils.select_part), 0))
with Row(scroll_layout) as row:
group_curves = Button(row.layout(), 'gsGroupCurves')
group_curves.set_label('分组
曲线')
group_curves.clicked.connect(undo(self.core.functions.group_curves))
regroup_by_layer = Button(row.layout(), 'gsRegroupByLayer')
regroup_by_layer.set_label('按图层
重新分组')
regroup_by_layer.clicked.connect(undo(self.core.functions.regroup_by_layer))
group_name = LineEdit('gsGroupNameTextField', scroll_layout)
group_name.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
group_name.set_auto_format(True)
group_name.setClearButtonEnabled(True)
group_name.setPlaceholderText('组名称')
custom_layer_names_colors = Button(scroll_layout, 'gsCustomLayerNamesAndColors')
custom_layer_names_colors.set_button_style('small-filled')
custom_layer_names_colors.set_label('图层名称和颜色')
custom_layer_names_colors.clicked.connect(self.custom_layer_colors_window.window)
scroll_layout.addWidget(separator())
scroll_layout.addWidget(wrap_control(mc.text(l='选择控制点')))
slider_widget = wrap_control(mc.floatSliderGrp('gsSelectCVSlider', w=1, min=0, max=1, step=0.05, dc=self.core.sliders.select_cv_slider, cc=self.core.sliders.release))
self.widget_manager.add('gsSelectCVSlider', slider_widget)
scroll_layout.addWidget(slider_widget)
with Row(scroll_layout) as row:
transfer_attributes = Button(row.layout(), 'gsTransferAttributes')
transfer_attributes.set_label('转移
属性.')
transfer_attributes.set_icon('mod-bottom')
transfer_attributes.clicked.connect(undo(self.core.attributes.transfer_attributes))
mc.popupMenu(mm=1, p=transfer_attributes.objectName())
mc.menuItem('gsCopyAttributes', rp='N', l='复制属性', aob=1, c=lambda _: self.core.attributes.copy_attributes())
mc.menuItem(ob=1, c=lambda _: self.attributes_filter_window.open_ui())
mc.menuItem('gsPasteAttributes', rp='S', l='粘贴属性', aob=1, c=lambda _: self.core.attributes.paste_attributes())
mc.menuItem(ob=1, c=lambda _: self.attributes_filter_window.open_ui())
transfer_uvs = Button(row.layout(), 'gsTransferUVs')
transfer_uvs.set_label('转移\nUV')
transfer_uvs.set_icon('mod-bottom')
transfer_uvs.clicked.connect(undo(self.core.attributes.transfer_uvs))
mc.popupMenu(mm=1, p=transfer_uvs.objectName())
mc.menuItem('gsCopyUVs', aob=1, rp='N', l='复制UV', c=lambda _: self.core.attributes.copy_uvs())
mc.menuItem(ob=1, c=lambda _: self.attributes_filter_window.open_ui())
mc.menuItem('gsPasteUVs', aob=1, rp='S', l='粘贴UV', c=lambda _: self.core.attributes.paste_uvs())
mc.menuItem(ob=1, c=lambda _: self.attributes_filter_window.open_ui())
reset_pivot = Button(row.layout(), 'gsResetPivot')
reset_pivot.set_label('重置\n轴心点')
reset_pivot.clicked.connect(undo(self.core.functions.reset_curve_pivot_point))
reset_pivot.set_icon('mod-bottom')
mc.popupMenu(mm=1, p=reset_pivot.objectName())
mc.menuItem('gsResetPivotToRoot', rp='N', l='重置到根部', c=lambda _: self.core.functions.reset_curve_pivot_point())
mc.menuItem('gsResetPivotToTip', rp='S', l='重置到尖端', c=lambda _: self.core.functions.reset_curve_pivot_point(2))
scroll_layout.addWidget(separator())
def rebuild_slider_release():
self.core.sliders.rebuild_slider_release()
self.core.sliders.release()
def rebuild_button_clicked():
self.core.sliders.rebuild_slider_drag()
rebuild_slider_release()
with Row(scroll_layout, margins=style.scale([1.5, 0, 1.5, 0])) as rebuild_reset_row:
rebuild_button = Button(obj_name='gsRebuildWithCurrentValue')
rebuild_button.set_button_style('small-filled')
rebuild_button.set_label('R', line_height=100)
rebuild_button.setMinimumWidth(1)
rebuild_button.setMaximumSize(*style.scale([16, 16]))
rebuild_button.clicked.connect(rebuild_button_clicked)
reset_button = Button(obj_name='gsResetRebuildSliderRange')
reset_button.set_button_style('small-filled')
reset_button.set_icon('reset')
reset_button.setMinimumWidth(1)
reset_button.setMaximumSize(*style.scale([16, 16]))
reset_button.clicked.connect(lambda: mc.intSliderGrp('gsRebuildSlider', e=1, min=1, max=50))
rrr = rebuild_reset_row.layout()
if isinstance(rrr, QHBoxLayout):
rrr.addWidget(rebuild_button, 1)
rrr.addWidget(wrap_control(mc.text(l='重建曲线')), 3)
rrr.addWidget(reset_button, 1)
rebuild_curve_slider = wrap_control(mc.intSliderGrp('gsRebuildSlider', f=1, cw=[(1, 32), (2, 28)], min=1, max=50, fmx=999, v=1, dc=self.core.sliders.rebuild_slider_drag, cc=lambda *_: rebuild_slider_release()))
self.widget_manager.add('gsRebuildSlider', rebuild_curve_slider)
scroll_layout.addWidget(rebuild_curve_slider)
with Row(scroll_layout) as row:
duplicate_curve = Button(row.layout(), 'gsDuplicateCurve')
duplicate_curve.set_label('复制')
duplicate_curve.clicked.connect(undo(self.core.functions.duplicate_curve))
randomize_curve = Button(row.layout(), 'gsRandomizeCurve')
randomize_curve.set_label('随机化')
randomize_curve.clicked.connect(self.randomize_curve_window.open_ui)
with Row(scroll_layout) as row:
extend_curve = Button(row.layout(), 'gsExtendCurve')
extend_curve.set_label('延伸')
extend_curve.clicked.connect(undo(self.core.functions.extend_curve))
reduce_curve = Button(row.layout(), 'gsReduceCurve')
reduce_curve.set_label('缩减')
reduce_curve.clicked.connect(undo(self.core.functions.reduce_curve))
with Row(scroll_layout) as row:
smooth = Button(row.layout(), 'gsSmooth')
smooth.set_label('平滑')
smooth.clicked.connect(undo(self.core.functions.smooth_curve))
smooth.set_icon('marking-top')
mc.popupMenu(mm=1, p=smooth.objectName())
mc.radioMenuItemCollection()
mc.menuItem('gsSmoothMult1', rp='N', rb=1, l='x1')
mc.menuItem('gsSmoothMult3', rp='E', rb=0, l='x3')
mc.menuItem('gsSmoothMult5', rp='S', rb=0, l='x5')
mc.menuItem('gsSmoothMult10', rp='W', rb=0, l='x10')
factor_slider = wrap_control(mc.floatSliderGrp('gsFactorSlider', l='因子', adj=3, w=1, cw=[(1, 32), (2, 28)], min=1, max=100))
self.widget_manager.add('gsFactorSlider', factor_slider)
scroll_layout.addWidget(factor_slider)
scroll_layout.addWidget(separator())
with Frame(scroll_layout, obj_name='MirrorFrame', label='镜像', margins=[1, 1, 1, 1]) as frame:
with Row(frame.get_frame_layout()) as row:
mirror_x = Button(row.layout(), 'mirrorX')
mirror_x.set_label('X')
mirror_x.set_font_size(16)
mirror_x.clicked.connect(partial(undo(self.core.functions.mirror_hair), 0))
mirror_y = Button(row.layout(), 'mirrorY')
mirror_y.set_label('Y')
mirror_y.set_font_size(16)
mirror_y.clicked.connect(partial(undo(self.core.functions.mirror_hair), 1))
mirror_z = Button(row.layout(), 'mirrorZ')
mirror_z.set_label('Z')
mirror_z.set_font_size(16)
mirror_z.clicked.connect(partial(undo(self.core.functions.mirror_hair), 2))
with Row(frame.get_frame_layout()) as row:
mirror_flip_grp = QtWidgets.QButtonGroup(row)
mirror = Button(row.layout(), 'mirrorRadio')
mirror.set_label('镜像')
mirror.set_button_style('small')
mirror.setCheckable(True)
mirror.setChecked(True)
flip = Button(row.layout(), 'flipRadio')
flip.set_label('翻转')
flip.set_button_style('small')
flip.setCheckable(True)
mirror_flip_grp.addButton(mirror)
mirror_flip_grp.addButton(flip)
scroll_layout.addWidget(separator())
with Row(scroll_layout) as row:
control_curve = Button(row.layout(), 'gsControlCurve')
control_curve.set_label('控制曲线')
control_curve.clicked.connect(undo(self.core.functions.control_curve_create))
apply_control_curve = Button(row.layout(), 'gsApplyControlCurve')
apply_control_curve.set_label('应用')
apply_control_curve.setFixedWidth(style.scale(48))
apply_control_curve.clicked.connect(undo(self.core.functions.control_curve_apply))
scroll_layout.addWidget(separator())
with Row(scroll_layout) as row:
curve_control_window = Button(row.layout(), 'gsCurveControlWindow')
curve_control_window.set_label('曲线控制窗口')
curve_control_window.pressed.connect(self.curve_control_window.open_ui)
scroll_layout.addWidget(separator())
with Row(scroll_layout) as row:
uv_editor = Button(row.layout(), 'gsUVEditorMain')
uv_editor.set_label('UV编辑器窗口')
uv_editor.pressed.connect(self.uv_editor.open_ui)
scroll_layout.addWidget(separator())
scroll_layout.addWidget(wrap_control(mc.text(l=VERSION)))
warp_switch.setChecked(self.options_manager.get('warpSwitch'))
extrude_switch.setChecked(not self.options_manager.get('warpSwitch'))
self.core.layer_manager.layer_collections.toggle_widget()
self.tooltips.toggle_custom_tooltips_main(self.options_manager.get('enableTooltips'))
def selection_sets(self, i, layout, label):
def toggle_geometry_edit(*_):
self.core.layer_manager.curve_geometry_edit_toggle(i)
self.core.updates.update_main_ui()
def toggle_curve_visibility(*_):
self.core.layer_manager.toggle_obj_visibility(i, 0)
self.core.updates.update_main_ui()
def toggle_geo_visibility(*_):
self.core.layer_manager.toggle_obj_visibility(i, 1)
self.core.updates.update_main_ui()
def toggle_layer_visibility(*_):
self.core.layer_manager.toggle_layer_visibility(i)
self.core.updates.update_main_ui()
sel_set = Layer(layout=layout, obj_name='curveGrp%s' % i)
sel_set.setStyleSheet(style.layer())
sel_set.set_label(str(label))
mc.popupMenu(mm=1, p=sel_set.objectName())
mc.menuItem(rp='N', l='将所选内容添加到图层', c=lambda _: self.core.layer_manager.curve_add_to_layer(i))
mc.menuItem(rp='NW', l='提取几何体', c=lambda _: self.core.functions.extract_curve_geo(i))
mc.menuItem(rp='NE', l='切换几何体编辑', c=toggle_geometry_edit)
mc.menuItem(rp='W', l='选择曲线', c=lambda _: self.core.layer_manager.curve_layer_select_obj(i, 0))
mc.menuItem(rp='E', l='选择几何体', c=lambda _: self.core.layer_manager.curve_layer_select_obj(i, 1))
mc.menuItem(rp='SW', l='切换曲线可见性', c=toggle_curve_visibility)
mc.menuItem(rp='SE', l='切换几何体可见性', c=toggle_geo_visibility)
mc.menuItem(rp='S', l='切换图层可见性', c=toggle_layer_visibility)
sel_set.clicked.connect(partial(undo(self.core.layer_manager.layer_clicked), i))
return sel_set
def extrude_warp_toggle(self):
"""Toggle the extrude/warp switch"""
buttons = ['newCard', 'newTube', 'curveCard', 'curveTube', 'addCards', 'addTubes']
button_style = style.BUTTON_NORMAL
if self.widget_manager.get('extrudeSwitch').isChecked():
button_style = style.BUTTON_NORMAL_BLUE_BORDER
for button in buttons:
self.widget_manager.get(button).setStyleSheet(button_style)
def update_layer_list(self):
"""Update the layer list"""
if self.widget_manager.exists('gsLayerSelector'):
self.widget_manager.get('gsLayerSelector').updateLayerList()
self.core.curve_control.update_ui()
def toggle_tooltips(self):
"""Toggle the custom tooltips"""
for widget in self.widget_manager.get_widgets():
if hasattr(widget, 'enable_tooltip') and callable(getattr(widget, 'enable_tooltip')):
widget.enable_tooltip(self.options_manager.get('enableTooltips'))
self.tooltips.toggle_custom_tooltips_main(self.options_manager.get('enableTooltips'))
self.tooltips.toggle_custom_tooltips_curve_control(self.options_manager.get('enableTooltips'))
self.tooltips.toggle_custom_tooltips_scale_factor(self.options_manager.get('enableTooltips'))