# 字节码版本: 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'))