"""
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.1 Studio
Copyright 2023, George Sladkovsky (Yehor Sladkovskyi)
All Rights Reserved
Autodesk Maya is a property of Autodesk, Inc.
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
"""
import os
import sys
from datetime import datetime
from functools import partial as pa
from imp import reload
import maya.cmds as mc
from PySide2 import QtCore, QtGui, QtWidgets
from . import core, ui
from . import constants
from .constants import *
from .utils import style, tooltips, utils, wrap
from .utils.utils import deferred, deferredLp, noUndo, undo
from .utils.wrap import WIDGETS
reload(core)
reload(utils)
reload(style)
reload(wrap)
reload(ui)
reload(tooltips)
reload(constants)
# Loggers
MESSAGE = utils.logger
LOGGER = utils.logger.logger
LOGGER.debug('-----------------Starting Log Session-----------------')
LOGGER.debug(' {} '.format(datetime.now().strftime("%d/%m/%Y, %H:%M:%S")))
### Interface Script Jobs ###
def checkScriptJobs(controlName):
""" Checks if script jobs exist and adds them if necessary """
jobNumbers = list()
workspaceExists = False
UI = 'control'
if mc.workspaceControl(controlName, exists=1):
workspaceExists = True
if controlName == MAIN_WINDOW_NAME:
UI = 'main'
elif controlName == UV_EDITOR_NAME:
UI = 'uv_editor'
elif controlName == SCALE_FACTOR_UI:
UI = 'scale_factor_ui'
jobList = mc.scriptJob(lj=1)
for job in jobList:
if controlName in job:
jobNumbers.append(job.split()[0][0:-1])
if len(jobNumbers) == 0 and workspaceExists:
scriptJobsInit(UI)
LOGGER.info('scriptJobs created for "%s" UI!' % UI)
elif len(jobNumbers) > 0 and not workspaceExists:
LOGGER.info('scriptJobs deleted!')
for number in jobNumbers:
mc.scriptJob(k=number)
def scriptJobsInit(uiName):
if uiName == 'main':
mc.scriptJob(per=1, p=MAIN_WINDOW_NAME, event=['SceneOpened', pa(checkScriptJobs, MAIN_WINDOW_NAME)])
mc.scriptJob(per=1, p=MAIN_WINDOW_NAME, event=['SceneOpened', pa(deferred(core.updateMainUI), True)])
mc.scriptJob(per=1, p=MAIN_WINDOW_NAME, event=['SceneOpened', deferredLp(core.onSceneOpenedUpdateLayerCount)])
mc.scriptJob(per=1, p=MAIN_WINDOW_NAME, event=['SelectionChanged', core.updateMainUI])
mc.scriptJob(per=1, p=MAIN_WINDOW_NAME, event=['Undo', core.updateMainUI])
elif uiName == 'control':
mc.scriptJob(per=1, p=CURVE_CONTROL_NAME, event=['SelectionChanged', core.curveControlUI.updateUI])
mc.scriptJob(per=1, p=CURVE_CONTROL_NAME, event=['Undo', deferred(core.curveControlUI.updateUI)])
elif uiName == 'uv_editor':
def updateUVs():
if (mc.workspaceControl(UV_EDITOR_NAME, q=1, ex=1) and
mc.workspaceControl(UV_EDITOR_NAME, q=1, r=1)):
ui.uveditor.updateEditor()
mc.scriptJob(per=1, p=UV_EDITOR_NAME, event=['SelectionChanged', updateUVs])
mc.scriptJob(per=1, p=UV_EDITOR_NAME, event=['Undo', updateUVs])
def main():
utils.checkNativePlugins(['curveWarp'], MAIN_WINDOW_NAME)
if mc.workspaceControl(MAIN_WINDOW_NAME, q=1, ex=1):
if MAYA_VER >= 2018:
if not mc.workspaceControl(MAIN_WINDOW_NAME, q=1, vis=1):
mc.workspaceControl(MAIN_WINDOW_NAME, e=1, rs=1)
core.updateMainUI()
else:
mc.workspaceControl(MAIN_WINDOW_NAME, e=1, vis=0)
else:
mc.workspaceControl(MAIN_WINDOW_NAME, e=1, fl=1)
mc.deleteUI(MAIN_WINDOW_NAME)
else:
CurveToolsUI()
mc.workspaceControl(MAIN_WINDOW_NAME, e=1, ui=UI_SCRIPT)
try:
core.toggleColor.checkColorStorageNode()
checkScriptJobs(MAIN_WINDOW_NAME)
utils.deferred(core.onSceneOpenedUpdateLayerCount)() # Also updates the UI
except Exception as e:
LOGGER.exception(e)
# Main UI
class CurveToolsUI(QtWidgets.QWidget):
def __init__(self):
WIDGETS.clear()
# Maya Native Workspace
parent = ui.mayaWorkspaceControl(name=MAIN_WINDOW_NAME, label=MAIN_WINDOW_LABEL)
# Resolve Fonts
fontDatabase = QtGui.QFontDatabase()
fontDatabase.removeAllApplicationFonts()
fonts = os.listdir(utils.getFolder.fonts())
for font in fonts:
fontDatabase.addApplicationFont(utils.getFolder.fonts() + font)
# Dockable Workspace Connection
super(CurveToolsUI, self).__init__(parent)
self.ui()
parent.layout().addWidget(self)
self.scrollWidget.setFocus()
checkScriptJobs(MAIN_WINDOW_NAME)
def ui(self):
# Layout
mainLayout = QtWidgets.QVBoxLayout(self)
mainLayout.setContentsMargins(*style.scale([2, 0, 2, 0]))
self.scrollWidget = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout(self.scrollWidget)
scrollArea = QtWidgets.QScrollArea()
scrollArea.setWidget(self.scrollWidget)
mainLayout.addWidget(scrollArea)
# Layout Settings
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(style.scale(2))
layout.setMargin(0)
layout.setAlignment(QtCore.Qt.AlignTop)
scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame)
scrollArea.setWidgetResizable(True)
# Menu buttons
menuBarWidget = QtWidgets.QWidget()
menuBarLayout = QtWidgets.QHBoxLayout(menuBarWidget)
menuBarLayout.setContentsMargins(0, 0, 0, 0)
menuBarLayout.setAlignment(QtCore.Qt.AlignCenter)
menuBar = QtWidgets.QMenuBar()
menuBar.setSizePolicy(PREFERRED_POLICY)
menuBarLayout.addWidget(menuBar)
layout.addWidget(menuBarWidget)
# Options Menu
with wrap.Menu('Options', menuBar) as menu:
menu.triggered.connect(core.saveOptions)
menu.addSection('Import / Export')
importCurves = wrap.MenuItem('importCurves', 'Import Curves', menu)
importCurves.triggered.connect(noUndo(core.importExport.importCurves))
exportCurves = wrap.MenuItem('exportCurves', 'Export Curves', menu)
exportCurves.triggered.connect(core.importExport.exportCurves)
menu.addSection('Global Modifiers')
changeScaleFactor = wrap.MenuItem('changeScaleFactor', 'Change Scale Factor', menu)
changeScaleFactor.triggered.connect(ui.scaleFactorWindow)
globalCurveThickness = wrap.MenuItem('globalCurveThickness', 'Global Curve Thickness', menu)
globalCurveThickness.triggered.connect(ui.curveThicknessWindow)
menu.addSection('Viewport Commands')
setAOSettings = wrap.MenuItem('setAOSettings', 'Set AO Settings', menu)
setAOSettings.triggered.connect(core.setAOSettings)
with wrap.Menu('Transparency Settings', menu) as transparencySettingsMenu:
simpleTransparency = wrap.MenuItem(
'setSimpleTransparency',
'Simple Transparency (Fast, Inncaurate)',
transparencySettingsMenu
)
simpleTransparency.triggered.connect(noUndo(pa(core.setTransparencySettings, 0)))
objectSortingTransparency = wrap.MenuItem(
'setObjectSortingTransparency',
'Object Sorting Transparency (Average)',
transparencySettingsMenu
)
objectSortingTransparency.triggered.connect(noUndo(pa(core.setTransparencySettings, 1)))
setDepthTransparency = wrap.MenuItem(
'setDepthTransparency',
'Depth Transparency (Accurate, Slow)',
transparencySettingsMenu
)
setDepthTransparency.triggered.connect(noUndo(pa(core.setTransparencySettings, 2)))
menu.addSection('Convert Curves')
with wrap.Menu('Convert Curves', menu) as convertCurvesSubmenu:
convertToWarpCard = wrap.MenuItem('convertToWarpCard', "Convert to Warp Card", convertCurvesSubmenu)
convertToWarpCard.triggered.connect(pa(undo(core.convertSelectionTo), 0))
convertToWarpTube = wrap.MenuItem('convertToWarpTube', "Convert to Warp Tube", convertCurvesSubmenu)
convertToWarpTube.triggered.connect(pa(undo(core.convertSelectionTo), 1))
convertToExtrudeCard = wrap.MenuItem('convertToExtrudeCard', "Convert to Extrude Card", convertCurvesSubmenu)
convertToExtrudeCard.triggered.connect(pa(undo(core.convertSelectionTo), 2))
convertToExtrudeTube = wrap.MenuItem('convertToExtrudeTube', "Convert to Extrude Tube", convertCurvesSubmenu)
convertToExtrudeTube.triggered.connect(pa(undo(core.convertSelectionTo), 3))
menu.addSection('General Options')
wrap.MenuItem('keepCurveAttributes', 'Keep Curve Attributes', menu, True, core.getOption('keepCurveAttributes'))
wrap.MenuItem('populateBlendAttributes', 'Add Cards/Tubes Blend Attributes',
menu, True, core.getOption('populateBlendAttributes'))
wrap.MenuItem('convertInstances', 'Auto Convert Instances', menu, True, core.getOption('convertInstances'))
wrap.MenuItem('useAutoRefineOnNewCurves', 'Use Auto-Refine on New Curves',
menu, True, core.getOption('useAutoRefineOnNewCurves'))
wrap.MenuItem('flipUVsAfterMirror', 'Flip UVs After Mirror',
menu, True, core.getOption('flipUVsAfterMirror'))
enableTooltipsMenu = wrap.MenuItem('enableTooltips', 'Enable Tooltips', menu, True, core.getOption('enableTooltips'))
enableTooltipsMenu.triggered.connect(self.toggleTooltips)
menu.addSection('Color Options')
syncColor = wrap.MenuItem('syncCurveColor', 'Sync Curve Color to Layer Color', menu, True, core.getOption('syncCurveColor'))
syncColor.triggered.connect(core.toggleColor.syncCurveColors)
wrap.MenuItem('colorizedRegroup', 'Colorize Regrouped Layers', menu, True, core.getOption('colorizedRegroup'))
colorOnlyDiffuse = wrap.MenuItem('colorOnlyDiffuse', 'Color Only Affects Diffuse',
menu, True, core.getOption('colorOnlyDiffuse'))
colorOnlyDiffuse.triggered.connect(core.toggleColor.updateColorOptions)
checkerPattern = wrap.MenuItem('checkerPattern', 'Checker Pattern for Color Mode', menu, True, core.getOption('checkerPattern'))
checkerPattern.triggered.connect(core.toggleColor.updateColorOptions)
menu.addSection('Bind Options')
wrap.MenuItem('boundCurvesFollowParent', 'Bound Curves Follow Parent', menu, True, core.getOption('boundCurvesFollowParent'))
wrap.MenuItem('massBindOption', 'Bind to All Available Empty Curves', menu, True, core.getOption('massBindOption'))
wrap.MenuItem('bindDuplicatesCurves', 'Duplicate Curves Before Bind', menu, True, core.getOption('bindDuplicatesCurves'))
wrap.MenuItem('bindFlipUVs', 'Flip UVs before Bind', menu, True, core.getOption('bindFlipUVs'))
menu.addSection('Layer Options')
wrap.MenuItem('ignoreLastLayer', 'Ignore Last Layer', menu, True, core.getOption('ignoreLastLayer'))
wrap.MenuItem('syncOutlinerLayerVis', 'Sync Outliner/Layer Visibility', menu, True, core.getOption('syncOutlinerLayerVis'))
wrap.MenuItem('replacingCurveLayerSelection', 'Replacing Curve Layer Selection', menu, True,
core.getOption('replacingCurveLayerSelection'))
onlyNumbersInLayers = wrap.MenuItem('layerNumbersOnly', 'Use Only Numbers in Layers', menu, True,
core.getOption('layerNumbersOnly'))
onlyNumbersInLayers.triggered.connect(core.changeLayersToNumbers)
onlyNumbersInLayers.triggered.connect(self.updateLayerList)
with wrap.Menu('Number of Active Layers', menu) as layerNumberMenu:
with wrap.ActionGroup('layerRowsActionGroup', layerNumberMenu) as actionGroup:
wrap.MenuItem('2layerRows', '20 Layers', layerNumberMenu, True,
core.getOption('2layerRows'), collection=actionGroup)
wrap.MenuItem('3layerRows', '30 Layers', layerNumberMenu, True,
core.getOption('3layerRows'), collection=actionGroup)
wrap.MenuItem('4layerRows', '40 Layers', layerNumberMenu, True,
core.getOption('4layerRows'), collection=actionGroup)
wrap.MenuItem('6layerRows', '60 Layers', layerNumberMenu, True,
core.getOption('6layerRows'), collection=actionGroup)
wrap.MenuItem('8layerRows', '80 Layers', layerNumberMenu, True,
core.getOption('8layerRows'), collection=actionGroup)
actionGroup.triggered.connect(core.updateMainUI)
menu.addSection('Layer Collection Options')
wrap.MenuItem('ignoreTemplateCollections', 'Ignore "Template" Collection Names', menu, True, core.getOption('ignoreTemplateCollections'))
wrap.MenuItem('groupTemplateCollections', 'Group "Template" Collections Together', menu, True, core.getOption('groupTemplateCollections'))
layerCollectionsToggle = wrap.MenuItem('showLayerCollectionsMenu', 'Show Layer Collections Menu',
menu, True, core.getOption('showLayerCollectionsMenu'))
layerCollectionsToggle.triggered.connect(core.layerCollections.toggleLayerCollectionsWidget)
wrap.MenuItem('importIntoANewCollection', 'Import Into a New Collection',
menu, True, core.getOption('importIntoANewCollection'))
menu.addSection('Other Options')
with wrap.Menu('Other Options', menu) as otherOptionsMenu:
otherOptionsMenu.addSection('Utility Commands')
convertToNewLayerSystem = wrap.MenuItem('convertToNewLayerSystem', 'Convert to New Layer System', otherOptionsMenu)
convertToNewLayerSystem.triggered.connect(utils.convertToNewLayerSystem)
updateLayers = wrap.MenuItem('updateLayers', 'Update Layers', otherOptionsMenu)
updateLayers.triggered.connect(undo(core.deleteUnusedLayers)) # TODO: Check if undo breaks anything
resetToDefaults = wrap.MenuItem('resetToDefaults', 'Reset to Defaults', otherOptionsMenu)
resetToDefaults.triggered.connect(utils.resetUI)
otherOptionsMenu.addSection('Fixes')
maya2020UVFix = wrap.MenuItem('maya2020UVFix', 'Fix Maya 2020-2022 UV Bug', otherOptionsMenu)
maya2020UVFix.triggered.connect(undo(utils.fixMaya2020UVs))
fixBrokenGraphs = wrap.MenuItem('mayaFixBrokenGraphs', 'Fix Broken Graphs', otherOptionsMenu)
fixBrokenGraphs.triggered.connect(undo(utils.fixBrokenGraphs))
convertBezierToNurbs = wrap.MenuItem('convertBezierToNurbs', 'Convert Selected Bezier to NURBS', otherOptionsMenu)
convertBezierToNurbs.triggered.connect(undo(utils.convertBezierToNurbs))
maya2020TwistFix = wrap.MenuItem('maya2020TwistAttribute', 'Fix Maya 2020.4 Twist Attribute', otherOptionsMenu)
maya2020TwistFix.triggered.connect(undo(utils.fixMaya2020Twist))
maya2020UnbindFix = wrap.MenuItem('maya2020UnbindFix', 'Fix Maya 2020.4 Unbind Function', otherOptionsMenu)
maya2020UnbindFix.triggered.connect(undo(utils.fixMaya2020Unbind))
deleteAllAnimationKeys = wrap.MenuItem('deleteAllAnimationKeys', 'Delete All Animation Keys', otherOptionsMenu)
deleteAllAnimationKeys.triggered.connect(undo(utils.deleteKeysOnAllObjects))
# Help Menu
with wrap.Menu('Help', menuBar) as menu:
openLogFile = wrap.MenuItem('openLogFile', 'Open Log File', menu)
openLogFile.triggered.connect(utils.logger.openLogFile)
openOnlineDocumentation = wrap.MenuItem('openOnlineDocumentation', 'Open Online Documentation', menu)
openOnlineDocumentation.triggered.connect(utils.openDocs)
usefulLinks = wrap.MenuItem('usefulLinks', 'Useful Links and Contacts', menu)
usefulLinks.triggered.connect(ui.about.socialWindow)
# About Menu
with wrap.Menu('About', menuBar) as menu:
aboutAction = wrap.MenuItem('gsAbout', 'About', menu)
aboutAction.triggered.connect(ui.about.aboutWindow)
menu.addSeparator()
menu.addAction('Made by George Sladkovsky (%s)' % YEAR).setEnabled(False)
# MAIN UI
layout.addWidget(wrap.separator())
# Extrude Warp Switch
with wrap.Row(layout) as row:
# Switch Group
extrudeWarpSwitchGroup = QtWidgets.QButtonGroup(layout)
WIDGETS['gsExtrudeWarpSwitchGroup'] = extrudeWarpSwitchGroup
extrudeWarpSwitchGroup.buttonToggled.connect(self.extrudeWarpToggle)
extrudeWarpSwitchGroup.buttonClicked.connect(core.saveOptions)
warpSwitch = wrap.Button(row.layout(), 'warpSwitch')
warpSwitch.setLabel('Warp', lineHeight=100)
warpSwitch.setButtonStyle('small')
warpSwitch.setCheckable(True)
extrudeSwitch = wrap.Button(row.layout(), 'extrudeSwitch')
extrudeSwitch.setLabel('Extrude', lineHeight=100)
extrudeSwitch.setButtonStyle('small')
extrudeSwitch.setCheckable(True)
extrudeWarpSwitchGroup.addButton(warpSwitch, 0)
extrudeWarpSwitchGroup.addButton(extrudeSwitch, 1)
# New Card and New Tube
with wrap.Row(layout) as row:
newCard = wrap.Button(row.layout(), 'newCard')
newCard.setLabel('New Card')
newCard.clicked.connect(pa(undo(core.create.new), 0))
newTube = wrap.Button(row.layout(), 'newTube')
newTube.setLabel('New Tube')
newTube.clicked.connect(pa(undo(core.create.new), 1))
# Convert Curve to Card and Convert Curve to Tube
with wrap.Row(layout) as row:
curveCard = wrap.Button(row.layout(), 'curveCard')
curveCard.setLabel('Curve Card')
curveCard.clicked.connect(pa(undo(core.create.multiple), 0))
curveTube = wrap.Button(row.layout(), 'curveTube')
curveTube.setLabel('Curve Tube')
curveTube.clicked.connect(pa(undo(core.create.multiple), 1))
# Bind and Unbind
with wrap.Row(layout) as row:
bind = wrap.Button(row.layout(), 'gsBind')
bind.setLabel('Bind')
bind.setIcon('mod-top')
bind.clicked.connect(undo(core.create.bind))
unbind = wrap.Button(row.layout(), 'gsUnbind')
unbind.setLabel('Unbind')
unbind.clicked.connect(undo(core.create.unbind))
layout.addWidget(wrap.separator())
# Add Curve and Add Tube
with wrap.Row(layout) as row:
addCards = wrap.Button(row.layout(), 'addCards')
addCards.setLabel('Add Card')
addCards.setIcon('mod-top')
addCards.clicked.connect(pa(undo(core.create.populate), 0))
addTubes = wrap.Button(row.layout(), 'addTubes')
addTubes.setLabel('Add Tube')
addTubes.setIcon('mod-top')
addTubes.clicked.connect(pa(undo(core.create.populate), 1))
# Fill and Multiply
with wrap.Row(layout) as row:
fill = wrap.Button(row.layout(), 'gsFill')
fill.setLabel('Fill')
fill.setIcon('mod-top')
fill.clicked.connect(pa(undo(core.create.fill)))
subdivide = wrap.Button(row.layout(), 'gsSubdivide')
subdivide.setLabel('Subdivide')
subdivide.setIcon('mod-top')
subdivide.clicked.connect(pa(undo(core.subdivideCurve)))
# Add Slider
with wrap.Row(layout) as row:
m_addCardsSlider = mc.intSliderGrp('gsCurvesSlider', l='Add', f=1, adj=3,
cw=[(1, 20), (2, 25), (3, 1)],
min=1, max=10, v=3)
addCardsSlider = wrap.mayaSlider(m_addCardsSlider, layout=row.layout())
WIDGETS['gsCurvesSlider'] = addCardsSlider
addCardsSlider.setContentsMargins(0, 0, 0, 0)
addCardsSlider.children()[2].setFocusPolicy(QtCore.Qt.NoFocus)
layout.addWidget(wrap.separator())
# Edge to Curve
with wrap.Row(layout) as row:
edgeToCurve = wrap.Button(row.layout(), 'gsEdgeToCurve')
edgeToCurve.setLabel('Edge To Curve')
edgeToCurve.clicked.connect(pa(undo(core.edgeToCurve)))
cardToCurve = wrap.Button(row.layout(), 'gsCardToCurve')
cardToCurve.setLabel('Card To Curve')
cardToCurve.clicked.connect(ui.cardToCurveWindow.openUI)
layout.addWidget(wrap.separator())
# Layer Collections Menu
with wrap.Row(layout, objName='LayerCollectionsLayout', spacing=1) as layerCollectionsLayout:
layerComboBox = wrap.LayerCollectionWidget('layerCollectionsComboBox')
layerComboBox.setStyleSheet(style.smallComboBox)
layerComboBox.currentIndexChanged.connect(lambda *_: deferred(core.updateMainUI)())
layerComboBox.setSizeAdjustPolicy(layerComboBox.AdjustToMinimumContentsLength)
layerComboBox.setDuplicatesEnabled(False)
layerComboBox.setMinimumWidth(0)
layerComboBox.setFixedHeight(style.scale(16))
mc.popupMenu(mm=1, p=layerComboBox.objectName())
mc.menuItem(rp='N', l='Clear', c=lambda *_: undo(core.layerCollections.clear)())
mc.menuItem(rp='E', l='Copy', c=lambda *_: noUndo(core.layerCollections.copy)())
mc.menuItem(rp='NE', l='Move Up', c=lambda *_: noUndo(core.layerCollections.moveUp)())
mc.menuItem(rp='NW', l='Merge Up', c=lambda *_: noUndo(core.layerCollections.mergeUp)())
mc.menuItem(rp='W', l='Paste', c=lambda *_: undo(core.layerCollections.paste)())
mc.menuItem(rp='SW', l='Merge Down', c=lambda *_: noUndo(core.layerCollections.mergeDown)())
mc.menuItem(rp='SE', l='Move Down', c=lambda *_: noUndo(core.layerCollections.moveDown)())
mc.menuItem(rp='S', l='Rename', c=lambda *_: noUndo(core.layerCollections.rename)())
with wrap.Row(layerCollectionsLayout.layout(), margins=style.scale([1, 0, 0, 0])) as plusMinusButtonsLayout:
layerComboPlus = wrap.Button(objName="layerCollectionsPlus", layout=plusMinusButtonsLayout.layout())
layerComboPlus.setFixedHeight(style.scale(16))
layerComboPlus.setLabel("+", lineHeight=100)
layerComboPlus.setMinimumWidth(1)
layerComboPlus.setButtonStyle("small-filled")
layerComboPlus.clicked.connect(core.layerCollections.createLayerCollection)
layerComboMinus = wrap.Button(objName="layerCollectionsMinus", layout=plusMinusButtonsLayout.layout())
layerComboMinus.setEnabled(False)
layerComboMinus.setFixedHeight(style.scale(16))
layerComboMinus.setMinimumWidth(1)
layerComboMinus.setLabel("-", lineHeight=100)
layerComboMinus.setButtonStyle("small-filled")
layerComboMinus.clicked.connect(undo(core.layerCollections.deleteLayerCollection))
layerCollectionsLayout.layout().addWidget(layerComboBox, 3)
layerCollectionsLayout.layout().addWidget(plusMinusButtonsLayout, 1)
layerComboBox.insertItem(0, "Main")
# Layer Filters
with wrap.Row(layout) as row:
allFilter = wrap.Button(row.layout(), 'gsAllFilter')
allFilter.setButtonStyle('small-filled')
allFilter.setLabel('All', lineHeight=100)
allFilter.setIcon('mod-top')
allFilter.clicked.connect(pa(undo(core.layersFilterToggle), True, True))
curveFilter = wrap.Button(row.layout(), 'gsCurveFilter')
curveFilter.setButtonStyle('small-filled')
curveFilter.setLabel('Curve', lineHeight=100)
curveFilter.clicked.connect(pa(undo(core.layersFilterToggle), True, False, ignore=["Shift+Ctrl"]))
mc.popupMenu(mm=1, p=curveFilter.objectName())
mc.menuItem(rp='N', l='Toggle Always on Top', c=lambda *_: undo(core.alwaysOnTopToggle)())
mc.menuItem(rp='S', l='Auto-Hide Curves on Inactive Collections',
cb=mc.optionVar(q='GSCT_AutoHideCurvesOnInactiveCollections'),
c=lambda cb: undo(core.collectionVisibilityToggle)(cb))
geoFilter = wrap.Button(row.layout(), 'gsGeoFilter')
geoFilter.setButtonStyle('small-filled')
geoFilter.setLabel('Geo', lineHeight=100)
geoFilter.clicked.connect(pa(undo(core.layersFilterToggle), False, True, ignore=["Shift+Ctrl"]))
colorMode = wrap.Button(row.layout(), 'colorMode')
colorMode.setButtonStyle('small-filled')
colorMode.setCheckable(True)
colorMode.setChecked(False)
colorMode.setLabel('Color', lineHeight=100)
colorMode.clicked.connect(pa(undo(core.toggleColor.toggleColorVis)))
mc.popupMenu(mm=1, p=colorMode.objectName())
mc.menuItem(rp='N', l='Randomize Colors', c=lambda *_: undo(core.toggleColor.randomizeColors)())
mc.menuItem(rp='E', l='Reset Curve Colors', c=lambda *_: core.toggleColor.resetCurveColors())
mc.menuItem(rp='W', l='Apply Curve Colors', c=lambda *_: core.toggleColor.syncCurveColors(True))
mc.menuItem(rp='S', l='Custom Colors Window', c=ui.customLayerColors.window)
# Layers
with wrap.Layout(layout, objName='LayerLayout') as layerLayout:
layerButtonGrp = QtWidgets.QButtonGroup(layerLayout.layout())
layerButtonGrp.setObjectName(wrap.getUniqueName('LayerGroup'))
layerButtonGrp.setExclusive(True)
WIDGETS['LayerGroup'] = layerButtonGrp
# First Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow0', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i, row.layout(), i))
# Second Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow1', spacing=0) as row:
letter = ord('A')
letters = [chr(i) for i in range(letter, letter + 10)]
if WIDGETS['layerNumbersOnly'].isChecked():
letters = [i for i in range(10, 20)]
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 10, row.layout(), letters[i]))
# Third Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow2', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 20, row.layout(), i + 20))
WIDGETS['layerRow2'].setHidden(True)
# Fourth Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow3', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 30, row.layout(), i + 30))
WIDGETS['layerRow3'].setHidden(True)
# Fifth Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow4', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 40, row.layout(), i + 40))
WIDGETS['layerRow4'].setHidden(True)
# Sixth Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow5', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 50, row.layout(), i + 50))
WIDGETS['layerRow5'].setHidden(True)
# Seventh Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow6', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 60, row.layout(), i + 60))
WIDGETS['layerRow6'].setHidden(True)
# Eighth Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow7', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 70, row.layout(), i + 70))
WIDGETS['layerRow7'].setHidden(True)
WIDGETS['curveGrp0'].setChecked(True)
# Extract Selected and Extract All
with wrap.Row(layout) as row:
extractSelected = wrap.Button(row.layout(), 'gsExtractSelected')
extractSelected.setLabel('Extract
Selected')
extractSelected.setIcon('mod-bottom')
extractSelected.clicked.connect(pa(undo(core.extractSelectedCurves)))
extractAll = wrap.Button(row.layout(), 'gsExtractAll')
extractAll.setLabel('Extract
All')
extractAll.setIcon('mod-bottom')
extractAll.clicked.connect(undo(core.extractAllCurves))
layout.addWidget(wrap.separator())
# Select Curve, Geo and Group
with wrap.Row(layout) as row:
selectCurve = wrap.Button(row.layout(), 'gsSelectCurve')
selectCurve.setLabel('Select
Curve')
selectCurve.clicked.connect(pa(undo(core.selectPart), 1))
selectGeo = wrap.Button(row.layout(), 'gsSelectGeo')
selectGeo.setLabel('Select
Geo')
selectGeo.clicked.connect(pa(undo(core.selectPart), 2))
selectGroup = wrap.Button(row.layout(), 'gsSelectGroup')
selectGroup.setLabel('Select
Group')
selectGroup.clicked.connect(pa(undo(core.selectPart), 0))
# Group Curves Button
with wrap.Row(layout) as row:
groupCurves = wrap.Button(row.layout(), 'gsGroupCurves')
groupCurves.setLabel('Group
Curves')
groupCurves.clicked.connect(undo(core.groupCurves))
regroupByLayer = wrap.Button(row.layout(), 'gsRegroupByLayer')
regroupByLayer.setLabel('Regroup
by Layer')
regroupByLayer.clicked.connect(undo(core.regroupByLayer))
# Group Name Input Field
groupName = wrap.LineEdit('gsGroupNameTextField', layout)
groupName.setAlignment(QtCore.Qt.AlignCenter)
groupName.setAutoFormat(True)
groupName.setClearButtonEnabled(True)
groupName.setPlaceholderText("Group Name")
# Custom Layer Names Window
customLayerNamesColors = wrap.Button(layout, 'gsCustomLayerNamesAndColors')
customLayerNamesColors.setButtonStyle('small-filled')
customLayerNamesColors.setLabel('Layer Names & Colors')
customLayerNamesColors.clicked.connect(ui.customLayerColors.window)
layout.addWidget(wrap.separator())
# Select CVs Label
layout.addWidget(wrap.wrapControl(mc.text(l='Select CVs')))
# Select CVs Slider
sliderWidget = wrap.wrapControl(
mc.floatSliderGrp(
'gsSelectCVSlider',
w=1, min=0, max=1, step=0.05,
dc=core.sliders.selectCVSlider,
cc=core.sliders.release
)
)
WIDGETS['gsSelectCVSlider'] = sliderWidget
layout.addWidget(sliderWidget)
# Select Curve, Geo and Group
with wrap.Row(layout) as row:
transferAttributes = wrap.Button(row.layout(), 'gsTransferAttributes')
transferAttributes.setLabel('Transfer
Attr.')
transferAttributes.setIcon('mod-bottom')
transferAttributes.clicked.connect(undo(core.attributes.transferAttr))
mc.popupMenu(mm=1, p=transferAttributes.objectName())
mc.menuItem('gsCopyAttributes', rp='N', l='Copy Attrs', aob=1, c=lambda _: core.attributes.copyAttributes())
mc.menuItem(ob=1, c=lambda _: ui.attributesFilter.openUI())
mc.menuItem('gsPasteAttributes', rp='S', l='Paste Attrs', aob=1, c=lambda _: core.attributes.pasteAttributes())
mc.menuItem(ob=1, c=lambda _: ui.attributesFilter.openUI())
transferUVs = wrap.Button(row.layout(), 'gsTransferUVs')
transferUVs.setLabel('Transfer
UVs')
transferUVs.setIcon('mod-bottom')
transferUVs.clicked.connect(undo(core.attributes.transferUV))
mc.popupMenu(mm=1, p=transferUVs.objectName())
mc.menuItem('gsCopyUVs', aob=1, rp='N', l='Copy UVs', c=lambda _: core.attributes.copyUVs())
mc.menuItem(ob=1, c=lambda _: ui.attributesFilter.openUI())
mc.menuItem('gsPasteUVs', aob=1, rp='S', l='Paste UVs', c=lambda _: core.attributes.pasteUVs())
mc.menuItem(ob=1, c=lambda _: ui.attributesFilter.openUI())
resetPivot = wrap.Button(row.layout(), 'gsResetPivot')
resetPivot.setLabel('Reset
Pivot')
resetPivot.clicked.connect(undo(core.resetCurvePivotPoint))
resetPivot.setIcon('mod-bottom')
mc.popupMenu(mm=1, p=resetPivot.objectName())
mc.menuItem('gsResetPivotToRoot', rp='N', l='Reset to Root', c=lambda _: core.resetCurvePivotPoint())
mc.menuItem('gsResetPivotToTip', rp='S', l='Reset to Tip', c=lambda _: core.resetCurvePivotPoint(2))
layout.addWidget(wrap.separator())
# Rebuild Curve
def rebuildSliderRelease(_):
core.sliders.rebuildSliderRelease()
core.sliders.release()
def rebuildButtonClicked():
core.sliders.rebuildSliderDrag()
rebuildSliderRelease(None)
with wrap.Row(layout, margins=style.scale([1.5, 0, 1.5, 0])) as rebuildResetRow:
rebuildButton = wrap.Button(objName='gsRebuildWithCurrentValue')
rebuildButton.setButtonStyle('small-filled')
rebuildButton.setLabel("R", lineHeight=100)
rebuildButton.setMinimumWidth(1)
rebuildButton.setMaximumSize(*style.scale([16, 16]))
rebuildButton.clicked.connect(rebuildButtonClicked)
resetButton = wrap.Button(objName='gsResetRebuildSliderRange')
resetButton.setButtonStyle('small-filled')
resetButton.setIcon('reset')
resetButton.setMinimumWidth(1)
resetButton.setMaximumSize(*style.scale([16, 16]))
resetButton.clicked.connect(lambda: mc.intSliderGrp('gsRebuildSlider', e=1, min=1, max=50))
rebuildResetRow.layout().addWidget(rebuildButton, 1)
rebuildResetRow.layout().addWidget(wrap.wrapControl(mc.text(l='Rebuild Curve')), 3)
rebuildResetRow.layout().addWidget(resetButton, 1)
rebuildCurveSlider = wrap.wrapControl(mc.intSliderGrp(
'gsRebuildSlider', f=1, cw=[(1, 32), (2, 28)], min=1, max=50, fmx=999, v=1,
dc=core.sliders.rebuildSliderDrag,
cc=rebuildSliderRelease))
WIDGETS['gsRebuildSlider'] = rebuildCurveSlider
layout.addWidget(rebuildCurveSlider)
# Duplicate and Randomize
with wrap.Row(layout) as row:
duplicateCurve = wrap.Button(row.layout(), 'gsDuplicateCurve')
duplicateCurve.setLabel('Duplicate')
duplicateCurve.clicked.connect(undo(core.duplicateCurve))
randomizeCurve = wrap.Button(row.layout(), 'gsRandomizeCurve')
randomizeCurve.setLabel('Randomize')
randomizeCurve.clicked.connect(ui.randomizeCurveWindow)
# Extend and Reduce
with wrap.Row(layout) as row:
extendCurve = wrap.Button(row.layout(), 'gsExtendCurve')
extendCurve.setLabel('Extend')
extendCurve.clicked.connect(undo(core.extendCurve))
reduceCurve = wrap.Button(row.layout(), 'gsReduceCurve')
reduceCurve.setLabel('Reduce')
reduceCurve.clicked.connect(undo(core.reduceCurve))
# Smooth
with wrap.Row(layout) as row:
smooth = wrap.Button(row.layout(), 'gsSmooth')
smooth.setLabel('Smooth')
smooth.clicked.connect(undo(core.smoothCurve))
smooth.setIcon('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')
# Smooth Slider
factorSlider = wrap.wrapControl(mc.floatSliderGrp(
'gsFactorSlider', l='Factor', adj=3, w=1, cw=[(1, 32), (2, 28)], min=1, max=100))
WIDGETS['gsFactorSlider'] = factorSlider
layout.addWidget(factorSlider)
layout.addWidget(wrap.separator())
# Mirroring
with wrap.Frame(layout, objName='MirrorFrame', label='Mirroring', margins=[1, 1, 1, 1]) as frame:
with wrap.Row(frame.getFrameLayout()) as row:
mirrorX = wrap.Button(row.layout(), 'mirrorX')
mirrorX.setLabel('X')
mirrorX.setFontSize(16)
mirrorX.clicked.connect(pa(undo(core.mirrorHair), 0))
mirrorY = wrap.Button(row.layout(), 'mirrorY')
mirrorY.setLabel('Y')
mirrorY.setFontSize(16)
mirrorY.clicked.connect(pa(undo(core.mirrorHair), 1))
mirrorZ = wrap.Button(row.layout(), 'mirrorZ')
mirrorZ.setLabel('Z')
mirrorZ.setFontSize(16)
mirrorZ.clicked.connect(pa(undo(core.mirrorHair), 2))
with wrap.Row(frame.getFrameLayout()) as row:
mirrorFlipGrp = QtWidgets.QButtonGroup(row.layout())
mirror = wrap.Button(row.layout(), 'mirrorRadio')
mirror.setLabel('Mirror')
mirror.setButtonStyle('small')
mirror.setCheckable(True)
mirror.setChecked(True)
flip = wrap.Button(row.layout(), 'flipRadio')
flip.setLabel('Flip')
flip.setButtonStyle('small')
flip.setCheckable(True)
mirrorFlipGrp.addButton(mirror)
mirrorFlipGrp.addButton(flip)
layout.addWidget(wrap.separator())
# Control Curve and Apply
with wrap.Row(layout) as row:
controlCurve = wrap.Button(row.layout(), 'gsControlCurve')
controlCurve.setLabel('Control Curve')
controlCurve.clicked.connect(undo(core.controlCurveCreate))
applyControlCurve = wrap.Button(row.layout(), 'gsApplyControlCurve')
applyControlCurve.setLabel('Apply')
applyControlCurve.setFixedWidth(style.scale(48))
applyControlCurve.clicked.connect(undo(core.controlCurveApply))
layout.addWidget(wrap.separator())
# Curve Control Window
with wrap.Row(layout) as row:
curveControlWindow = wrap.Button(row.layout(), 'gsCurveControlWindow')
curveControlWindow.setLabel('Curve Control Window')
curveControlWindow.pressed.connect(ui.curveControlWorkspace)
layout.addWidget(wrap.separator())
# UV Editor Window
with wrap.Row(layout) as row:
uvEditor = wrap.Button(row.layout(), 'gsUVEditorMain')
uvEditor.setLabel('UV Editor Window')
uvEditor.pressed.connect(ui.uvEditorWorkspace)
layout.addWidget(wrap.separator())
# Version
layout.addWidget(wrap.wrapControl(mc.text(l=core.VERSION)))
# Toggling the correct switch
warpSwitch.setChecked(core.getOption('warpSwitch'))
extrudeSwitch.setChecked(not core.getOption('warpSwitch'))
# Toggling layer collections widget
core.layerCollections.toggleLayerCollectionsWidget()
# Setting the custom tooltips
tooltips.toggleCustomTooltipsMain(core.getOption('enableTooltips'))
def selectionSets(self, i, layout, label): # Creates layer button
def toggleGeometryEdit(*_):
core.curveGeometryEditToggle(i)
core.updateMainUI()
def toggleCurveVisibility(*_):
core.toggleObjVisibility(i, 0)
core.updateMainUI()
def toggleGeoVisibility(*_):
core.toggleObjVisibility(i, 1)
core.updateMainUI()
def toggleLayerVisibility(*_):
core.toggleLayerVisibility(i)
core.updateMainUI()
selSet = wrap.Layer(layout=layout, objName='curveGrp%s' % i)
selSet.setStyleSheet(style.layer())
selSet.setLabel(str(label))
mc.popupMenu(mm=1, p=selSet.objectName())
mc.menuItem(rp='N', l='Add Selection to Layer', c=lambda _: core.curveAddToLayer(i))
mc.menuItem(rp='NW', l='Extract Geometry', c=lambda _: core.extractCurveGeo(i))
mc.menuItem(rp='NE', l='Toggle Geometry Edit', c=toggleGeometryEdit)
mc.menuItem(rp='W', l='Select Curves', c=lambda _: core.curveLayerSelectObj(i, 0))
mc.menuItem(rp='E', l='Select Geometry', c=lambda _: core.curveLayerSelectObj(i, 1))
mc.menuItem(rp='SW', l='Toggle Curve Visibility', c=toggleCurveVisibility)
mc.menuItem(rp='SE', l='Toggle Geo Visibility', c=toggleGeoVisibility)
mc.menuItem(rp='S', l='Toggle Layer Visibility', c=toggleLayerVisibility)
selSet.clicked.connect(pa(undo(core.layerClicked), i))
return selSet
def extrudeWarpToggle(self):
buttons = ['newCard', 'newTube', 'curveCard', 'curveTube', 'addCards', 'addTubes']
buttonStyle = style.buttonNormal
if WIDGETS['extrudeSwitch'].isChecked():
buttonStyle = style.buttonNormalBlueBorder
for button in buttons:
WIDGETS[button].setStyleSheet(buttonStyle)
def updateLayerList(self):
if 'gsLayerSelector' in WIDGETS:
WIDGETS['gsLayerSelector'].updateLayerList()
core.curveControlUI.updateUI()
def toggleTooltips(self):
for widget in WIDGETS:
if hasattr(WIDGETS[widget], "enableTooltip") and callable(getattr(WIDGETS[widget], "enableTooltip")):
WIDGETS[widget].enableTooltip(core.getOption('enableTooltips'))
tooltips.toggleCustomTooltipsMain(core.getOption('enableTooltips'))
tooltips.toggleCustomTooltipsCurveControl(core.getOption('enableTooltips'))